/* A Bison parser, made by GNU Bison 2.3.  */

/* Skeleton implementation for Bison's Yacc-like parsers in C

   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
   Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.  */

/* As a special exception, you may create a larger work that contains
   part or all of the Bison parser skeleton and distribute that work
   under terms of your choice, so long as that work isn't itself a
   parser generator using the skeleton or a modified version thereof
   as a parser skeleton.  Alternatively, if you modify or redistribute
   the parser skeleton itself, you may (at your option) remove this
   special exception, which will cause the skeleton and the resulting
   Bison output files to be licensed under the GNU General Public
   License without this special exception.

   This special exception was added by the Free Software Foundation in
   version 2.2 of Bison.  */

/* C LALR(1) parser skeleton written by Richard Stallman, by
   simplifying the original so-called "semantic" parser.  */

/* All symbols defined below should begin with yy or YY, to avoid
   infringing on user name space.  This should be done even for local
   variables, as they might otherwise be expanded by user macros.
   There are some unavoidable exceptions within include files to
   define necessary library symbols; they are noted "INFRINGES ON
   USER NAME SPACE" below.  */

/* Identify Bison output.  */
#define YYBISON 1

/* Bison version.  */
#define YYBISON_VERSION "2.3"

/* Skeleton name.  */
#define YYSKELETON_NAME "yacc.c"

/* Pure parsers.  */
#define YYPURE 0

/* Using locations.  */
#define YYLSP_NEEDED 0



/* Tokens.  */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
   /* Put the tokens into the symbol table, so that GDB and other debuggers
      know about them.  */
   enum yytokentype {
     ATTNAME = 258,
     BAD = 259,
     CLASSNAME = 260,
     COMMA = 261,
     DIGIT = 262,
     KEYWORD = 263,
     ENDLINE = 264,
     IS = 265,
     LSQUARE = 266,
     NONTERMINAL = 267,
     ONECHAR = 268,
     OPTIONAL = 269,
     OR = 270,
     RSQUARE = 271,
     SEMICOLON = 272,
     TERMINAL = 273,
     TERMINALSTRING = 274,
     REPSYMBOL = 275,
     TWOCHAR = 276
   };
#endif
/* Tokens.  */
#define ATTNAME 258
#define BAD 259
#define CLASSNAME 260
#define COMMA 261
#define DIGIT 262
#define KEYWORD 263
#define ENDLINE 264
#define IS 265
#define LSQUARE 266
#define NONTERMINAL 267
#define ONECHAR 268
#define OPTIONAL 269
#define OR 270
#define RSQUARE 271
#define SEMICOLON 272
#define TERMINAL 273
#define TERMINALSTRING 274
#define REPSYMBOL 275
#define TWOCHAR 276




/* Copy the first part of user declarations.  */



/******************************************************************************
  DISCLAIMER:
  This software was produced by the National Institute of Standards
  and Technology (NIST), an agency of the U.S. government, and by statute is
  not subject to copyright in the United States.  Recipients of this software
  assume all responsibility associated with its operation, modification,
  maintenance, and subsequent redistribution.

  See NIST Administration Manual 4.09.07 b and Appendix I. 
*****************************************************************************/

#include <stdio.h>   // for stderr
#include <string.h>  // for strcat
#include <stdlib.h>  // for malloc, free
#include "ebnfClasses.hh" // for debnf classes, declarations

#define YYERROR_VERBOSE
#define YYDEBUG 1
#define CLASSSIZE 400
#define NAMESIZE 256
#define LETTERSIZE 200

/********************************************************************/

/* PRELIMINARY NOTES

The in-line documentation in this file provides a wealth of details.
Start with the documentation of the main function.

Debnf2pars is highly tuned to DMIS. It is not a general-purpose EBNF
file processor.

1. What This Does

This debnf2pars.y file may be processed automatically (together with
ebnfClasses.hh and ebnfClasses.cc) to produce an executable debnf2pars
utility that reads an EBNF file describing the DMIS language and
writes a number of software files (as described below) for dealing
with DMIS (checking for errors, checking for conformance to a
conformance class, and building DMIS applications).

The debnf2pars executable reads dmis.debnf and writes the following
software files
        allSubAtts.cc - 9,121 lines
        assignMasterSubAtts.cc - 1,634 lines
        dmisConformanceChecker.cc - 49,622 lines
        dmisConformanceTester.cc - 67,228 lines
        dmis.hh - 53,838 lines
        dmis.cc - 49,251 lines
        dmis.y - 14,848 lines
        dmis.lex - 1,291 lines
The dmis.y file is processed by bison and the dmis.lex file is processed
by flex to produce three additional software files
        dmisLex.cc - 10,015 lines
        dmisYACC.cc - 32,915 lines
        dmisYACC.hh - 2,523 lines
The allSubAtts.cc file is copied to make 27 duplicates (one for each of
3 levels of the 2 Application Profiles and 7 addenda). Then each of
the 27 files is hand-edited to produce a C++ data file representing what
is allowed in one level of one Application Profile or addendum. The editing
is done solely by commenting out some portions. The 27 hand-edited files
are consumed by generateMore (and other executables) to make two additional
sofware files:
        assignModuleSubAtts.cc - 5,661 lines
        levelsSet.cc - 4,231 lines
        
The dmis.hh, dmis.cc, dmisLex.cc, dmisYACC.cc, and dmisYACC.hh files are
compiled together into a library, dmis.a. The library provides an API
with the following functionality.
 i . define structures to represent DMIS,
 ii. get and set components of the structures,
 iii. build a parse tree using the structures when a DMIS input file is read,
 iv. print a DMIS input file from a parse tree.

The automatically generated software is combined with hand-written
software to produce a parser, a conformance checker, and a conformance
tester.  The two conformance utilities will handle all (98,304)
combinations of levels of DMIS Application Profiles and addenda.  The
conformance class against which the conformance checker and tester
check DMIS input files is determined by the arguments in the command
that starts the checker or tester. For example the command

     dmisConformanceTester rmeas3.dmi PM2 IP2 QI1

checks the file rmeas3.dmi using prismatic AP level2, IPV addendum level 2,
and QIS addendum level 1.

A. Parser

The dmis.a library is combined with (hand-written) dmisParser.cc to
produce the parser.  The parser checks the syntax of DMIS input
files. The parser may be called two ways:

 i. With an argument that is the name of a DMIS input file. In this
    case the parser parses the file, reports errors and warnings
    (if there are any), and then reprints the file from the parse
    tree that is built during parsing if there are no errors.
 ii. With an argument that is the name of a file containing the names
    of a set of DMIS input files. In this case, the parser parses
    each DMIS input file and reports errors and warnings for each file
    (if there are any) but does not reprint the file.

B. Conformance Tester

To make the conformance tester, the dmis.a library is combined with:
 dmisConformanceTesterStart.cc (hand-written) 
 dmisConformanceTester.cc
 levelsSet.cc
 27 xxLists.cc files
 assignMasterSubAtts.cc
 assignModuleSubAtts.cc

The conformance tester reads one DMIS input file and:
1. checks for syntax errors.
2. Reports the number of syntax errors and warnings for the file.
3. If there are no syntax errors, the conformance tester:
   a. checks whether the file contains items that are not in the conformance
      class. For each item used but not in the conformance class (if there
      are any such items), an error message explaining the error in terms
      of C++ classes is printed, and the portion of DMIS code that caused
      the error is printed.
   b. reports the number of conformance class errors found in the file.
   c. reports the minimum conformance modules needed to make the file
      conform.
   d. reports the first C++ class items encountered in the file that
      caused each minimum conformance module to be selected.

For example:

   0 parser errors
   0 parser warnings

   no reportItem_HUMID subtype of reportItem
   HUMID

   no text attribute of reportStm
   R(R30)=REPORT/OP(OP1),'Jack'

   no text attribute of reportStm
   R(R31)=REPORT/TL(TL1),CC(CC1),DV(DV1),Q(Q1),'That''s all, folks!'


   3 conformance checker errors

   TW4 IP2 QI2 
   TW4 reportItem_HUMID
   IP2 cutcomParam
   QI2 dmeidStm


C. Conformance Checker

To make the conformance checker, the dmis.a library is combined with:
 dmisConformanceCheckerStart.cc (hand-written) 
 dmisConformanceChecker.cc
 27 xxLists.cc files
 assignMasterSubAtts.cc
 assignModuleSubAtts.cc

Like the parser, the conformance checker may be called two ways.

 i. With an argument that is the name of a DMIS input file. In this
    case the conformance checker parses the file, reports errors and
    warnings (if there are any), and then reports any parts of any
    of the DMIS code that are not allowed in the conformance class.
 ii. With an argument that is the name of a file containing the names
    of a set of DMIS input files. In this case, the conformance checker
    processes each DMIS input file as described above and then reports:
    a. the number of times each DMIS statement allowed in the conformance
       class was used.
    b. the percentage of DMIS statements in the conformance class that
       were used in the DMIS input files fed to the checker.
    c. the names of DMIS statements in the conformance class not used in
       any of the files.
    d. the names of any DMIS statements that were used in any of the DMIS
       input files that are not allowed in the conformance class.

In the conformance checker, a "DMIS statement" is any EBNF production
all of whose definitions end with a newline.  In general, these are
the items for which there is a subsection of section 6 of the DMIS
spec. In three cases (CALIB, RECALL, and SAVE) there is more than one
DMIS statement for a section 6 subsection. In three cases (CONST,
RMEAS, and SNSDEF), there is only one DMIS statement for multiple
subsections.

2. Conflict Detection

This file includes code for detecting and fixing seven situations in
which shift/reduce conflicts will arise in an automatically generated
YACC/Lex parser if the YACC is generated in a straightforward manner
from natural EBNF. The names of the EBNF productions that get fixed
are printed. In DMIS there are about 50 instances (total) of the seven
situations.

The fixing is done by changing the internal representation of the EBNF
before the YACC is generated from it.

In all of the situations, a shift/reduce conflict arises because of
the use of commas as secondary separators.

The code here could be generalized by replacing newline with "primary
separator" and comma with "secondary separator". The idea is that
the language being parsed is statement-based with each statement being
ended by a primary separator. Also, within each statement, a secondary
separator is usually used between tokens.

3. TokenNames and TokenLexes

The tokenNames contain the names of tokens as given implicitly in the
DEBNF file by using names in all upper case.  The tokenLexes correspond
one-to-one with the tokenNames and point to the same strings, except
where an explicit spelling of a token name is given in the DEBNF file.
Explicit spellings in the DEBNF file differ from the implicit spellings.
They are not required to differ, but there is no point in giving an
explicit spelling if it does not differ from the implicit spelling.
Differences occur when the spelling rules of EBNF (a name must start
with a letter) make an actual DMIS name illegal.

4. Expressions

The following tokens defined in the tokens section of this file may be
used as theType of an expression.
   ENDLINE
   KEYWORD
   NONTERMINAL
   ONECHAR
   OPTIONAL
   TERMINAL
   TERMINALSTRING
   TWOCHAR

5. Commas

The YACC grammar below is very straightforward except in its handling of
commas. Because EBNF uses commas to separate terms, it becomes very hard
for humans to read when quoted commas are mixed with unquoted commas.
For example: "device, ',', DMIS". It is convenient to use "c" instead
of ',' so the example becomes "device, c, DMIS", which is easy to read.

a. In the EBNF file, there is required to be a production
"c = ',' ;" that defines c to be a comma.

b. When the production defining c in the EBNF file is read, no production
is constructed internally for it, but the token C is recorded.

c. Everywhere else in the EBNF file that a comma in single quotes might
appear, either c or a comma in single quotes may be used.

d. Rather than creating a new expression every time a c is read
in the EBNF there is a special expression named commaExp that represents
a c.

e. In the YACC file that is generated, C is used rather than ',' , and
in the lex file that is generated, when a comma is read, C is returned.

f. The term COMMA in this file and in debnf2pars.lex refers to a literal
unquoted comma in the EBNF that serves to separate terms, such as the
two commas in "device, c, DMIS".

g. The language for which a parser is being constructed may not use C
as a token.

6. Parse Tree

The parsers this write include YACC actions that build a parse tree.
To make that possible, this includes:

a. automatically generating a C++ header file (baseName.hh)
defining classes with which a parse tree can be constructed.

b. writing YACC actions in the YACC file (baseName.y) that build a
parse tree using the classes. A great deal of code here is devoted to
printing YACC actions.

7. Pretty Printer

This includes code for generating a pretty printer that may be used to
reprint a DMIS file from the parse tree. The pretty printer is
implemented by having a printSelf method in every class defined in the
baseName.hh file and automatically generating a baseName.cc file
containing the code for all the printSelf functions.

8. Naming

In order to produce a user-friendly C++ application programming interface
(API), debnf2pars includes a lot of functions that generate names for
classes, data members of classes, and access functions. Most of the
automatically generated names are intuitively clear, but some are not.
Therefore, provisions have been made as follows for letting a human assign
names by putting comments containing names into the EBNF file. The names
automatically propagate from the EBNF file into the C++ code.

An attribute (data member) name may be assigned to any expression in a
debnf file that is a NONTERMINAL or a TERMINAL by following the expression
with a comment of the form (*A=xxx*), where the xxx is the attribute name.

Class names can (optionally) be assigned to definitions in the EBNF. This
is done by putting a comment of the form (*C=xxx*) in the EBNF after
the definition, where xxx is the class name to be assigned.

Warning: If a list item has only one definition, the definition should
not have a class name assigned in the EBNF that is different from from
the production name of the list item, since then the extraction of
the parentName in printYaccForFixList2 will be incorrect.

9. YACC and C++ in this file

Although this is a YACC file (debnf2pars.y), less than 2% of the file is
YACC. The other 98% is C++. This file is processed by bison to produce
the file debnf2parsYACC.cc, which is all C++ code. The C++ code in this
file is simply transcribed by bison to the debnf2parsYACC.cc file.
This file uses C++ code for EBNF classes from the files ebnfClasses.hh
and ebnfClasses.cc.

10. Changes in allSubAtts.cc

If allSubAtts.cc changes, the 27 xxLists.cc files that are edited copies
must be revised by making the same changes. Then assignModuleSubAtts.cc
and levelsSet.cc must be regenerated. The re-editing of the xxLists.cc
files has been partially automated. 

*/

/********************************************************************/

/*

The attCell class and the classData class are used to hold the data
from which code files for the conformance checker and the conformance
tester are printed. attCell has the data for one attribute of a
class. classData has the data for one class.

attCell has a next pointer so that it is easy to make a bare-bones
list of attCells.

In the classData class, either subs is empty, or atts is empty, or both.
Having that be the case was a strategic decision in the design of the
classes generated by the generator. It simplifies further processing
a lot.

*/

class attCell
{
public:
  enum sort {lst, ptr, simple};

  attCell()
    {
      attName = 0;
      typeSort = simple;
      typeName = 0;
      next = 0;
    }
  attCell(
    char * attNameIn,
    sort typeSortIn,
    char * typeNameIn)
    {
      attName = attNameIn;
      typeSort = typeSortIn;
      typeName = typeNameIn;
      next = 0;
    }

  char * attName;  // the name of the attribute
  sort typeSort;   // if type is a pointer (ptr), a list (lst), or not (simple)
  char * typeName; // the name of the type
  attCell * next;  // the next attribute in the list
};

class classData
{
public:
  classData()
    {
      name = 0;
      isBlock = false;
      subs = 0;
      atts = 0;
    }
  classData(
    char * nameIn,
    bool isBlockIn)
    {
      name = nameIn;
      isBlock = isBlockIn;
      subs = 0;
      atts = 0;
    }
  ~classData(){}
  char * name;       // the name of the class
  bool isBlock;      // whether the class represents a DMIS block
  stringCell * subs; // the names of the subclasses of the class
  attCell * atts;    // the attributes of the class
};

/********************************************************************/

extern FILE * yyin;
extern int yylex();

prodList productions;
expression * commaExp = new expression(0, strdup("commaExp"), 0, 0, 0);
expression * nullExp = new expression(0, strdup("nullExp"), 0, 0, 0);
expression * trueExp = new expression(0, strdup("trueExp"), 0, 0, 0);
expression * falseExp = new expression(0, strdup("falseExp"), 0, 0, 0);
expression * equalSignExp = new expression(NONTERMINAL,
					   strdup("equalSign"), 0, 0, 0);
stringCell statementNames;
char buffer[NAMESIZE];
char * tokenNames[26][LETTERSIZE];
char * tokenLexes[26][LETTERSIZE];
char * terminalNames[LETTERSIZE];

/********************************************************************/

void addData();
bool alwaysFollowedByComma(production * prod);
bool alwaysFollowedByCommaInExps(production * prod, expList * exps);
bool alwaysFollowedByNewline(production * prod, int depth);
void checkDuplicates(defList * defs);
bool expIsComma(expListCell * expCell);
bool expIsNewline(expListCell * expCell);
bool expIsProd(expListCell * expCell, production * prod);
int expListsSame(expList * exps1, expList * exps2);
void findAttributeNames(expList * exps);
classData * findClassData(char * className,
			  classData * classDatas[26][CLASSSIZE]);
void findClassNames(production * prod);
bool findEndsInOptional(production * prod);
bool findIsBlock(production * prod);
char * findKeyWord(expList * exps);
int findOptType(optional * opt);
int findPlaceCheck(expList * exps1, expList * exps2);
production * findProd(const char * itemName);
prodList * findProdSet(defList * defs);
void findSupertypes(production * prod);
int findToken(char * text, int * n);
void fixConflictList(production * testList);
void fixConflictListNested(production * outerList, production * outerItem,
			   production * innerList, production * innerItem);
void fixConflictListNested1(production * outerList, production * outerItem,
			    production * innerList, production * innerItem);
void fixConflictListNested2(production * theList, production * listItem);
void fixConflictListOptional(production * theList, production * listItem);
void fixConflictOther(prodListCell * prodCell, production * user,
		      production ** prodC, int * remove);
void fixConflictOther1(prodListCell * prodCell, production * user,
		       production ** prodC);
void fixConflictOther2(defList * userDefs, defList * userUserDefs,
		       prodListCell * prodCell, production * user,
		       production ** prodC);
void fixConflicts1();
void fixConflicts2();
void fixConflictsUsers(prodListCell * prodCell);
void flattenOpts(expList * exps, expList * flatExps); 
void insertItemAndCommaInList(production * listItem, production * theList);
void insertItemInList(production * listItem, production * theList, 
		      bool commaInside);
void makeClassDataAtts(expList * flatExps, classData * data);
void makeClassDataSubs(production * prod,
		       classData * classDatas[26][CLASSSIZE]);
void makeNewDefs(defList * defins);
void makeProdC(prodListCell * prodCell, production ** prodC);
int prepareDefForPlain(defListCell * defCell, defList * defs);
void prepareDefsForPlain(defList * defs);
void prepareStatementNames();
void printConfAllSubAtts(classData * classDatas[26][CLASSSIZE]);
void printConfAnalyzeItems(FILE * checker);
void printConfArrays(classData * classDatas[26][CLASSSIZE], FILE * checker);
void printConfAttChecker(attCell * aCell, char * className,
			 classData * classDatas[26][CLASSSIZE],FILE * checker);
void printConfBlockAttChecker(attCell * atts, char * className,
			      classData * classDatas[26][CLASSSIZE],
			      FILE * checker);
void printConfBlockSubChecker(stringCell * subs, char * className,
			      classData * classDatas[26][CLASSSIZE],
			      FILE * checker);
void printConfChecker(classData * classDatas[26][CLASSSIZE]);
void printConfFunctions(classData * classDatas[26][CLASSSIZE], FILE * checker);
void printConfAssignMaster(classData * classDatas[26][CLASSSIZE]);
void printConfReportSummary(FILE * checker);
void printConfReportSummaryFull(FILE * checker);
void printConfStart(classData * classDatas[26][CLASSSIZE], FILE * checker);
void printConfSubChecker(stringCell * strCell, char * className,
			 classData * classDatas[26][CLASSSIZE],FILE * checker);
void printConfTester(classData * classDatas[26][CLASSSIZE]);
void printCppBaseClass(char * baseClassName, FILE * cppHFile, FILE * cppCFile);
void printCppClassConstructorArgs(expList * flatExps, int * printMe,
				  const char *  spaces, FILE * aFile);
void printCppClassConstructors(char * className, expList * flatExps,
			       FILE * cppHFile, FILE * cppCFile);
void printCppClassData(classData * data, FILE * cppHFile);
void printCppClassDerived(char * className, char * parentName,
			  definition * def, classData * data,
			  FILE * cppHFile, FILE * cppCFile);
void printCppClassDestructor(char * className, FILE * cppHFile,
			     FILE * cppCFile);
void printCppClassDoc(expList * exps, FILE * cppHFile);
void printCppClasses(char * baseFileName,
                     classData * classDatas[26][CLASSSIZE],
		     prodList * toPrint);
void printCppClassMethods(char * className, classData * data,
			  FILE * cppHFile, FILE * cppCFile);
void printCppClassParent(char * className, prodList * subtypeOf,
			 char * baseClassName, FILE * cppHFile,
			 FILE * cppCFile);
void printCppClassPrinter(char * className, expList * exps,
			  expList * flatExps, FILE * cppCFile);
void printCppClassPrinterListNo(char * typeName, char * attName,
				int spaces, int noIf, FILE * cppHFile);
void printCppClassPrinterListYes(char * typeName, char * attName,
				 int spaces, FILE * cppHFile);
void printCppClassPrinterOpt(optional * opt, expListCell ** eCell,
			     int spaces, FILE * cppCFile);
void printCppClassPrinterOpt1(expList * exps, expListCell ** eCell,
			      int spaces, FILE * cppCFile);
void printCppClassPrinterOpt2(expList * exps, char * attName,
			      int spaces, FILE * cppCFile);
void printCppClassStart(const char * className, FILE * cppHFile,
			FILE * cppCFile);
void printCppClassTop(char * className, prodList * subtypeOf,
		      char * baseClassName, definition * def,
		      classData * data, FILE * cppHFile, FILE * cppCFile);
void printCppDocumentation(FILE * cppHFile);
void printCppNames(char * baseClassName, classData * classDatas[26][CLASSSIZE],
		   FILE * cppHFile);
void printCppProductionClasses(production * prod, char * baseClassName,
			       classData * classDatas[26][CLASSSIZE],
			       FILE * cppHFile, FILE * cppCFile);
void printLex(char * baseFileName);
void printLexEnd(FILE * lexFile);
void printLexMiddle(FILE * lexFile);
void printLexStart(char * baseFileName, FILE * lexFile);
void printLexString(const char * leader, const char * lexString,
		    const char * trailer, FILE * lexFile);
void printLexToken(char * theName, char * lexName, FILE * lexFile);
void printStarLine(FILE * someFile);
void printTestAttChecker(attCell * aCell, char * className,
			 classData * classDatas[26][CLASSSIZE], FILE * tester);
void printTestBlockAttChecker(attCell * atts, char * className,
			      classData * classDatas[26][CLASSSIZE],
			      FILE * checker);
void printTestBlockSubChecker(stringCell * subs, char * className,
			      classData * classDatas[26][CLASSSIZE],
			      FILE * checker);
void printTestFunctions(classData * classDatas[26][CLASSSIZE], FILE * checker);
void printTestStart(classData * classDatas[26][CLASSSIZE], FILE * tester);
void printTestSubChecker(stringCell * strCell, char * className,
			 classData * classDatas[26][CLASSSIZE], FILE * tester);
void printUpcase(char * text, FILE * aFile);
void printYacc(char * baseFileName);
void printYaccAction(char * prodName, char * className,
		     expList * exps, FILE * yaccFile);
void printYaccActionItem(expression * exp, int * commaFlag,
			 int * n, FILE * yaccFile);
void printYaccDatMinor(definition * def, char orOrSpace,
		       int commaIndex, const char * datName, FILE * yaccFile);
void printYaccDoCall(FILE * yaccFile);
void printYaccDoLabel(FILE * yaccFile);
void printYaccDoMacro(FILE * yaccFile);
void printYaccExpression(expression * exp, FILE * yaccFile);
void printYaccFindCallArgs(FILE * yaccFile);
void printYaccFindMacro(FILE * yaccFile);
void printYaccFindMacroArgs(FILE * yaccFile);
void printYaccFirstAction(char * className, expList * exps, FILE * yaccFile);
void printYaccFirstProduction(production * prod, FILE * yaccFile);
void printYaccForFixDef1(production * prod, defListCell * defCell,
			 char * parentName, char orOrSpace, FILE * yaccFile);
void printYaccForFixDef2(char * prodName, defListCell * defCell,
			 char * parentName, char orOrSpace, FILE * yaccFile);
void printYaccForFixList1(char * prodName, defList * defins, FILE * yaccFile);
void printYaccForFixList2(production * prod, defList * defins,
			  FILE * yaccFile);
void printYaccForNewDefs(char * prodName, defList * defins, FILE * yaccFile);
void printYaccForListDefault(char * listName, defList * defins,
			     FILE * yaccFile);
void printYaccForPlain(production * prod, FILE * yaccFile);
void printYaccForSupertype(char * prodName, defList * defins, FILE * yaccFile);
void printYaccGetStatement(FILE * yaccFile);
void printYaccGlobals(FILE * yaccFile);
void printYaccHandleLabel(FILE * yaccFile);
void printYaccIncDefs(FILE * yaccFile, char * baseFileName);
void printYaccInsertCalledMacro(FILE * yaccFile);
void printYaccIsCall(FILE * yaccFile);
void printYaccIsEndmac(FILE * yaccFile);
void printYaccIsMacro(FILE * yaccFile);
void printYaccLabelDefinition(char * className, expList * expressions,
			      bool labelNameTwo, FILE * yaccFile);
void printYaccMacroClass(FILE * yaccFile);
void printYaccMiddle(FILE * yaccFile);
void printYaccParseDmis(FILE * yaccFile);
void printYaccPreprocess(FILE * yaccFile);
void printYaccProduction(production * prod, bool labelNameTwo, FILE * yaccFile);
void printYaccProductions(FILE * yaccFile);
void printYaccProductionsStart(FILE * yaccFile);
void printYaccRule(expList * exps, char orOrSpace, FILE * yaccFile);
void printYaccStart(FILE * yaccFile, char * baseFileName);
void printYaccStringClasses(FILE * yaccFile);
void printYaccUnionAndTypes(FILE * yaccFile);
void printYaccWarn(FILE * yaccFile);
void printYaccYyerror(FILE * yaccFile);
bool prodIsLabel(production * prod);
void recordClass(char * className, bool isBlock,
		 classData * classDatas[26][CLASSSIZE]);
void recordClasses(prodList * toPrint, classData * classDatas[26][CLASSSIZE]);
void recordTerminal(char * terminalName);
void recordToken(char * tokenName);
void removeCommaInExps(production * prod, expList * exps);
int replaceOptInDefinition(defListCell * defCell, defList * defs);
void replaceOptMulti(defListCell * defCell, defList * defs,
		     expList * exps, expListCell * optCell, int digit);
void replaceOptsInDefinitions(defList * defs);
void replaceProdC(defList * defins, production * prod,
		  production * prodC, bool replaceEnd);
void reviseSpelling();
void selectProductions(prodList * toPrint);
int yyerror(char * s);
int yyparse();

/********************************************************************/



/* Enabling traces.  */
#ifndef YYDEBUG
# define YYDEBUG 0
#endif

/* Enabling verbose error messages.  */
#ifdef YYERROR_VERBOSE
# undef YYERROR_VERBOSE
# define YYERROR_VERBOSE 1
#else
# define YYERROR_VERBOSE 0
#endif

/* Enabling the token table.  */
#ifndef YYTOKEN_TABLE
# define YYTOKEN_TABLE 0
#endif

#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE

{
        double        dval;
        expression *  exal;
        definition *  fval;
        defList *     ldvl;
        expList *     levl;
        int           ival;
        optional *    oval;
        production *  pval;
        char *        sval;
}
/* Line 187 of yacc.c.  */

	YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
# define YYSTYPE_IS_TRIVIAL 1
#endif



/* Copy the second part of user declarations.  */


/* Line 216 of yacc.c.  */


#ifdef short
# undef short
#endif

#ifdef YYTYPE_UINT8
typedef YYTYPE_UINT8 yytype_uint8;
#else
typedef unsigned char yytype_uint8;
#endif

#ifdef YYTYPE_INT8
typedef YYTYPE_INT8 yytype_int8;
#elif (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
typedef signed char yytype_int8;
#else
typedef short int yytype_int8;
#endif

#ifdef YYTYPE_UINT16
typedef YYTYPE_UINT16 yytype_uint16;
#else
typedef unsigned short int yytype_uint16;
#endif

#ifdef YYTYPE_INT16
typedef YYTYPE_INT16 yytype_int16;
#else
typedef short int yytype_int16;
#endif

#ifndef YYSIZE_T
# ifdef __SIZE_TYPE__
#  define YYSIZE_T __SIZE_TYPE__
# elif defined size_t
#  define YYSIZE_T size_t
# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
#  define YYSIZE_T size_t
# else
#  define YYSIZE_T unsigned int
# endif
#endif

#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)

#ifndef YY_
# if YYENABLE_NLS
#  if ENABLE_NLS
#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
#   define YY_(msgid) dgettext ("bison-runtime", msgid)
#  endif
# endif
# ifndef YY_
#  define YY_(msgid) msgid
# endif
#endif

/* Suppress unused-variable warnings by "using" E.  */
#if ! defined lint || defined __GNUC__
# define YYUSE(e) ((void) (e))
#else
# define YYUSE(e) /* empty */
#endif

/* Identity function, used to suppress warnings about constant conditions.  */
#ifndef lint
# define YYID(n) (n)
#else
#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
static int
YYID (int i)
#else
static int
YYID (i)
    int i;
#endif
{
  return i;
}
#endif

#if ! defined yyoverflow || YYERROR_VERBOSE

/* The parser invokes alloca or malloc; define the necessary symbols.  */

# ifdef YYSTACK_USE_ALLOCA
#  if YYSTACK_USE_ALLOCA
#   ifdef __GNUC__
#    define YYSTACK_ALLOC __builtin_alloca
#   elif defined __BUILTIN_VA_ARG_INCR
#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
#   elif defined _AIX
#    define YYSTACK_ALLOC __alloca
#   elif defined _MSC_VER
#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
#    define alloca _alloca
#   else
#    define YYSTACK_ALLOC alloca
#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
#     ifndef _STDLIB_H
#      define _STDLIB_H 1
#     endif
#    endif
#   endif
#  endif
# endif

# ifdef YYSTACK_ALLOC
   /* Pacify GCC's `empty if-body' warning.  */
#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
#  ifndef YYSTACK_ALLOC_MAXIMUM
    /* The OS might guarantee only one guard page at the bottom of the stack,
       and a page size can be as small as 4096 bytes.  So we cannot safely
       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
       to allow for a few compiler-allocated temporary stack slots.  */
#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
#  endif
# else
#  define YYSTACK_ALLOC YYMALLOC
#  define YYSTACK_FREE YYFREE
#  ifndef YYSTACK_ALLOC_MAXIMUM
#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
#  endif
#  if (defined __cplusplus && ! defined _STDLIB_H \
       && ! ((defined YYMALLOC || defined malloc) \
	     && (defined YYFREE || defined free)))
#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
#   ifndef _STDLIB_H
#    define _STDLIB_H 1
#   endif
#  endif
#  ifndef YYMALLOC
#   define YYMALLOC malloc
#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
#   endif
#  endif
#  ifndef YYFREE
#   define YYFREE free
#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
void free (void *); /* INFRINGES ON USER NAME SPACE */
#   endif
#  endif
# endif
#endif /* ! defined yyoverflow || YYERROR_VERBOSE */


#if (! defined yyoverflow \
     && (! defined __cplusplus \
	 || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))

/* A type that is properly aligned for any stack member.  */
union yyalloc
{
  yytype_int16 yyss;
  YYSTYPE yyvs;
  };

/* The size of the maximum gap between one aligned stack and the next.  */
# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)

/* The size of an array large to enough to hold all stacks, each with
   N elements.  */
# define YYSTACK_BYTES(N) \
     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
      + YYSTACK_GAP_MAXIMUM)

/* Copy COUNT objects from FROM to TO.  The source and destination do
   not overlap.  */
# ifndef YYCOPY
#  if defined __GNUC__ && 1 < __GNUC__
#   define YYCOPY(To, From, Count) \
      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
#  else
#   define YYCOPY(To, From, Count)		\
      do					\
	{					\
	  YYSIZE_T yyi;				\
	  for (yyi = 0; yyi < (Count); yyi++)	\
	    (To)[yyi] = (From)[yyi];		\
	}					\
      while (YYID (0))
#  endif
# endif

/* Relocate STACK from its old location to the new one.  The
   local variables YYSIZE and YYSTACKSIZE give the old and new number of
   elements in the stack, and YYPTR gives the new location of the
   stack.  Advance YYPTR to a properly aligned location for the next
   stack.  */
# define YYSTACK_RELOCATE(Stack)					\
    do									\
      {									\
	YYSIZE_T yynewbytes;						\
	YYCOPY (&yyptr->Stack, Stack, yysize);				\
	Stack = &yyptr->Stack;						\
	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
	yyptr += yynewbytes / sizeof (*yyptr);				\
      }									\
    while (YYID (0))

#endif

/* YYFINAL -- State number of the termination state.  */
#define YYFINAL  11
/* YYLAST -- Last index in YYTABLE.  */
#define YYLAST   32

/* YYNTOKENS -- Number of terminals.  */
#define YYNTOKENS  22
/* YYNNTS -- Number of nonterminals.  */
#define YYNNTS  10
/* YYNRULES -- Number of rules.  */
#define YYNRULES  27
/* YYNRULES -- Number of states.  */
#define YYNSTATES  41

/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
#define YYUNDEFTOK  2
#define YYMAXUTOK   276

#define YYTRANSLATE(YYX)						\
  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)

/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
static const yytype_uint8 yytranslate[] =
{
       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
      15,    16,    17,    18,    19,    20,    21
};

#if YYDEBUG
/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
   YYRHS.  */
static const yytype_uint8 yyprhs[] =
{
       0,     0,     3,     5,     7,    10,    14,    17,    20,    23,
      25,    29,    31,    34,    35,    37,    41,    43,    46,    48,
      51,    53,    55,    57,    59,    61,    63,    67
};

/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
static const yytype_int8 yyrhs[] =
{
      23,     0,    -1,    24,    -1,    25,    -1,    24,    25,    -1,
      26,    27,    17,    -1,    12,    10,    -1,    18,    10,    -1,
       8,    10,    -1,    28,    -1,    27,    15,    28,    -1,    29,
      -1,    29,     5,    -1,    -1,    30,    -1,    29,     6,    30,
      -1,     8,    -1,    12,     3,    -1,    12,    -1,    18,     3,
      -1,    18,    -1,    19,    -1,    13,    -1,    21,    -1,    31,
      -1,     9,    -1,    11,    29,    16,    -1,     7,    20,    11,
      29,    16,    -1
};

/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
static const yytype_uint16 yyrline[] =
{
       0,   672,   672,   676,   682,   691,   697,   699,   701,   706,
     708,   714,   716,   722,   723,   725,   731,   734,   736,   741,
     744,   747,   749,   751,   753,   755,   760,   762
};
#endif

#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
static const char *const yytname[] =
{
  "$end", "error", "$undefined", "ATTNAME", "BAD", "CLASSNAME", "COMMA",
  "DIGIT", "KEYWORD", "ENDLINE", "IS", "LSQUARE", "NONTERMINAL", "ONECHAR",
  "OPTIONAL", "OR", "RSQUARE", "SEMICOLON", "TERMINAL", "TERMINALSTRING",
  "REPSYMBOL", "TWOCHAR", "$accept", "dmisGrammar", "productionList",
  "production", "leftSide", "definitionList", "definition",
  "expressionList", "expression", "optional", 0
};
#endif

# ifdef YYPRINT
/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
   token YYLEX-NUM.  */
static const yytype_uint16 yytoknum[] =
{
       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
     275,   276
};
# endif

/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
static const yytype_uint8 yyr1[] =
{
       0,    22,    23,    24,    24,    25,    26,    26,    26,    27,
      27,    28,    28,    29,    29,    29,    30,    30,    30,    30,
      30,    30,    30,    30,    30,    30,    31,    31
};

/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
static const yytype_uint8 yyr2[] =
{
       0,     2,     1,     1,     2,     3,     2,     2,     2,     1,
       3,     1,     2,     0,     1,     3,     1,     2,     1,     2,
       1,     1,     1,     1,     1,     1,     3,     5
};

/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
   means the default is an error.  */
static const yytype_uint8 yydefact[] =
{
       0,     0,     0,     0,     0,     2,     3,    13,     8,     6,
       7,     1,     4,     0,    16,    25,    13,    18,    22,    20,
      21,    23,     0,     9,    11,    14,    24,     0,     0,    17,
      19,    13,     5,    12,     0,    13,    26,    10,    15,     0,
      27
};

/* YYDEFGOTO[NTERM-NUM].  */
static const yytype_int8 yydefgoto[] =
{
      -1,     4,     5,     6,     7,    22,    23,    24,    25,    26
};

/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
   STATE-NUM.  */
#define YYPACT_NINF -17
static const yytype_int8 yypact[] =
{
      -4,    -1,     7,    14,    25,    -4,   -17,    -6,   -17,   -17,
     -17,   -17,   -17,     6,   -17,   -17,    -6,    24,   -17,    26,
     -17,   -17,     1,   -17,    17,   -17,   -17,    19,     4,   -17,
     -17,    -6,   -17,   -17,    -6,    -6,   -17,   -17,   -17,     5,
     -17
};

/* YYPGOTO[NTERM-NUM].  */
static const yytype_int8 yypgoto[] =
{
     -17,   -17,   -17,    23,   -17,   -17,     0,   -16,    -2,   -17
};

/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
   positive, shift that token.  If negative, reduce the rule which
   number is the opposite.  If zero, do what YYDEFACT says.
   If YYTABLE_NINF, syntax error.  */
#define YYTABLE_NINF -1
static const yytype_uint8 yytable[] =
{
      28,    13,    14,    15,     1,    16,    17,    18,     2,     8,
      34,    34,    19,    20,     3,    21,    31,     9,    32,    39,
      36,    40,    33,    34,    10,    11,    27,    29,    12,    30,
      35,    37,    38
};

static const yytype_uint8 yycheck[] =
{
      16,     7,     8,     9,     8,    11,    12,    13,    12,    10,
       6,     6,    18,    19,    18,    21,    15,    10,    17,    35,
      16,    16,     5,     6,    10,     0,    20,     3,     5,     3,
      11,    31,    34
};

/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
   symbol of state STATE-NUM.  */
static const yytype_uint8 yystos[] =
{
       0,     8,    12,    18,    23,    24,    25,    26,    10,    10,
      10,     0,    25,     7,     8,     9,    11,    12,    13,    18,
      19,    21,    27,    28,    29,    30,    31,    20,    29,     3,
       3,    15,    17,     5,     6,    11,    16,    28,    30,    29,
      16
};

#define yyerrok		(yyerrstatus = 0)
#define yyclearin	(yychar = YYEMPTY)
#define YYEMPTY		(-2)
#define YYEOF		0

#define YYACCEPT	goto yyacceptlab
#define YYABORT		goto yyabortlab
#define YYERROR		goto yyerrorlab


/* Like YYERROR except do call yyerror.  This remains here temporarily
   to ease the transition to the new meaning of YYERROR, for GCC.
   Once GCC version 2 has supplanted version 1, this can go.  */

#define YYFAIL		goto yyerrlab

#define YYRECOVERING()  (!!yyerrstatus)

#define YYBACKUP(Token, Value)					\
do								\
  if (yychar == YYEMPTY && yylen == 1)				\
    {								\
      yychar = (Token);						\
      yylval = (Value);						\
      yytoken = YYTRANSLATE (yychar);				\
      YYPOPSTACK (1);						\
      goto yybackup;						\
    }								\
  else								\
    {								\
      yyerror (YY_("syntax error: cannot back up")); \
      YYERROR;							\
    }								\
while (YYID (0))


#define YYTERROR	1
#define YYERRCODE	256


/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
   If N is 0, then set CURRENT to the empty location which ends
   the previous symbol: RHS[0] (always defined).  */

#define YYRHSLOC(Rhs, K) ((Rhs)[K])
#ifndef YYLLOC_DEFAULT
# define YYLLOC_DEFAULT(Current, Rhs, N)				\
    do									\
      if (YYID (N))                                                    \
	{								\
	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
	}								\
      else								\
	{								\
	  (Current).first_line   = (Current).last_line   =		\
	    YYRHSLOC (Rhs, 0).last_line;				\
	  (Current).first_column = (Current).last_column =		\
	    YYRHSLOC (Rhs, 0).last_column;				\
	}								\
    while (YYID (0))
#endif


/* YY_LOCATION_PRINT -- Print the location on the stream.
   This macro was not mandated originally: define only if we know
   we won't break user code: when these are the locations we know.  */

#ifndef YY_LOCATION_PRINT
# if YYLTYPE_IS_TRIVIAL
#  define YY_LOCATION_PRINT(File, Loc)			\
     fprintf (File, "%d.%d-%d.%d",			\
	      (Loc).first_line, (Loc).first_column,	\
	      (Loc).last_line,  (Loc).last_column)
# else
#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
# endif
#endif


/* YYLEX -- calling `yylex' with the right arguments.  */

#ifdef YYLEX_PARAM
# define YYLEX yylex (YYLEX_PARAM)
#else
# define YYLEX yylex ()
#endif

/* Enable debugging if requested.  */
#if YYDEBUG

# ifndef YYFPRINTF
#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
#  define YYFPRINTF fprintf
# endif

# define YYDPRINTF(Args)			\
do {						\
  if (yydebug)					\
    YYFPRINTF Args;				\
} while (YYID (0))

# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
do {									  \
  if (yydebug)								  \
    {									  \
      YYFPRINTF (stderr, "%s ", Title);					  \
      yy_symbol_print (stderr,						  \
		  Type, Value); \
      YYFPRINTF (stderr, "\n");						  \
    }									  \
} while (YYID (0))


/*--------------------------------.
| Print this symbol on YYOUTPUT.  |
`--------------------------------*/

/*ARGSUSED*/
#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
static void
yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
#else
static void
yy_symbol_value_print (yyoutput, yytype, yyvaluep)
    FILE *yyoutput;
    int yytype;
    YYSTYPE const * const yyvaluep;
#endif
{
  if (!yyvaluep)
    return;
# ifdef YYPRINT
  if (yytype < YYNTOKENS)
    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
# else
  YYUSE (yyoutput);
# endif
  switch (yytype)
    {
      default:
	break;
    }
}


/*--------------------------------.
| Print this symbol on YYOUTPUT.  |
`--------------------------------*/

#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
static void
yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
#else
static void
yy_symbol_print (yyoutput, yytype, yyvaluep)
    FILE *yyoutput;
    int yytype;
    YYSTYPE const * const yyvaluep;
#endif
{
  if (yytype < YYNTOKENS)
    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
  else
    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);

  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
  YYFPRINTF (yyoutput, ")");
}

/*------------------------------------------------------------------.
| yy_stack_print -- Print the state stack from its BOTTOM up to its |
| TOP (included).                                                   |
`------------------------------------------------------------------*/

#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
static void
yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
#else
static void
yy_stack_print (bottom, top)
    yytype_int16 *bottom;
    yytype_int16 *top;
#endif
{
  YYFPRINTF (stderr, "Stack now");
  for (; bottom <= top; ++bottom)
    YYFPRINTF (stderr, " %d", *bottom);
  YYFPRINTF (stderr, "\n");
}

# define YY_STACK_PRINT(Bottom, Top)				\
do {								\
  if (yydebug)							\
    yy_stack_print ((Bottom), (Top));				\
} while (YYID (0))


/*------------------------------------------------.
| Report that the YYRULE is going to be reduced.  |
`------------------------------------------------*/

#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
static void
yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
#else
static void
yy_reduce_print (yyvsp, yyrule)
    YYSTYPE *yyvsp;
    int yyrule;
#endif
{
  int yynrhs = yyr2[yyrule];
  int yyi;
  unsigned long int yylno = yyrline[yyrule];
  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
	     yyrule - 1, yylno);
  /* The symbols being reduced.  */
  for (yyi = 0; yyi < yynrhs; yyi++)
    {
      fprintf (stderr, "   $%d = ", yyi + 1);
      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
		       &(yyvsp[(yyi + 1) - (yynrhs)])
		       		       );
      fprintf (stderr, "\n");
    }
}

# define YY_REDUCE_PRINT(Rule)		\
do {					\
  if (yydebug)				\
    yy_reduce_print (yyvsp, Rule); \
} while (YYID (0))

/* Nonzero means print parse trace.  It is left uninitialized so that
   multiple parsers can coexist.  */
int yydebug;
#else /* !YYDEBUG */
# define YYDPRINTF(Args)
# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
# define YY_STACK_PRINT(Bottom, Top)
# define YY_REDUCE_PRINT(Rule)
#endif /* !YYDEBUG */


/* YYINITDEPTH -- initial size of the parser's stacks.  */
#ifndef	YYINITDEPTH
# define YYINITDEPTH 200
#endif

/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
   if the built-in stack extension method is used).

   Do not make this value too large; the results are undefined if
   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
   evaluated with infinite-precision integer arithmetic.  */

#ifndef YYMAXDEPTH
# define YYMAXDEPTH 10000
#endif



#if YYERROR_VERBOSE

# ifndef yystrlen
#  if defined __GLIBC__ && defined _STRING_H
#   define yystrlen strlen
#  else
/* Return the length of YYSTR.  */
#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
static YYSIZE_T
yystrlen (const char *yystr)
#else
static YYSIZE_T
yystrlen (yystr)
    const char *yystr;
#endif
{
  YYSIZE_T yylen;
  for (yylen = 0; yystr[yylen]; yylen++)
    continue;
  return yylen;
}
#  endif
# endif

# ifndef yystpcpy
#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
#   define yystpcpy stpcpy
#  else
/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
   YYDEST.  */
#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
static char *
yystpcpy (char *yydest, const char *yysrc)
#else
static char *
yystpcpy (yydest, yysrc)
    char *yydest;
    const char *yysrc;
#endif
{
  char *yyd = yydest;
  const char *yys = yysrc;

  while ((*yyd++ = *yys++) != '\0')
    continue;

  return yyd - 1;
}
#  endif
# endif

# ifndef yytnamerr
/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
   quotes and backslashes, so that it's suitable for yyerror.  The
   heuristic is that double-quoting is unnecessary unless the string
   contains an apostrophe, a comma, or backslash (other than
   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
   null, do not copy; instead, return the length of what the result
   would have been.  */
static YYSIZE_T
yytnamerr (char *yyres, const char *yystr)
{
  if (*yystr == '"')
    {
      YYSIZE_T yyn = 0;
      char const *yyp = yystr;

      for (;;)
	switch (*++yyp)
	  {
	  case '\'':
	  case ',':
	    goto do_not_strip_quotes;

	  case '\\':
	    if (*++yyp != '\\')
	      goto do_not_strip_quotes;
	    /* Fall through.  */
	  default:
	    if (yyres)
	      yyres[yyn] = *yyp;
	    yyn++;
	    break;

	  case '"':
	    if (yyres)
	      yyres[yyn] = '\0';
	    return yyn;
	  }
    do_not_strip_quotes: ;
    }

  if (! yyres)
    return yystrlen (yystr);

  return yystpcpy (yyres, yystr) - yyres;
}
# endif

/* Copy into YYRESULT an error message about the unexpected token
   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
   including the terminating null byte.  If YYRESULT is null, do not
   copy anything; just return the number of bytes that would be
   copied.  As a special case, return 0 if an ordinary "syntax error"
   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
   size calculation.  */
static YYSIZE_T
yysyntax_error (char *yyresult, int yystate, int yychar)
{
  int yyn = yypact[yystate];

  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
    return 0;
  else
    {
      int yytype = YYTRANSLATE (yychar);
      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
      YYSIZE_T yysize = yysize0;
      YYSIZE_T yysize1;
      int yysize_overflow = 0;
      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
      int yyx;

# if 0
      /* This is so xgettext sees the translatable formats that are
	 constructed on the fly.  */
      YY_("syntax error, unexpected %s");
      YY_("syntax error, unexpected %s, expecting %s");
      YY_("syntax error, unexpected %s, expecting %s or %s");
      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
# endif
      char *yyfmt;
      char const *yyf;
      static char const yyunexpected[] = "syntax error, unexpected %s";
      static char const yyexpecting[] = ", expecting %s";
      static char const yyor[] = " or %s";
      char yyformat[sizeof yyunexpected
		    + sizeof yyexpecting - 1
		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
		       * (sizeof yyor - 1))];
      char const *yyprefix = yyexpecting;

      /* Start YYX at -YYN if negative to avoid negative indexes in
	 YYCHECK.  */
      int yyxbegin = yyn < 0 ? -yyn : 0;

      /* Stay within bounds of both yycheck and yytname.  */
      int yychecklim = YYLAST - yyn + 1;
      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
      int yycount = 1;

      yyarg[0] = yytname[yytype];
      yyfmt = yystpcpy (yyformat, yyunexpected);

      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
	  {
	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
	      {
		yycount = 1;
		yysize = yysize0;
		yyformat[sizeof yyunexpected - 1] = '\0';
		break;
	      }
	    yyarg[yycount++] = yytname[yyx];
	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
	    yysize_overflow |= (yysize1 < yysize);
	    yysize = yysize1;
	    yyfmt = yystpcpy (yyfmt, yyprefix);
	    yyprefix = yyor;
	  }

      yyf = YY_(yyformat);
      yysize1 = yysize + yystrlen (yyf);
      yysize_overflow |= (yysize1 < yysize);
      yysize = yysize1;

      if (yysize_overflow)
	return YYSIZE_MAXIMUM;

      if (yyresult)
	{
	  /* Avoid sprintf, as that infringes on the user's name space.
	     Don't have undefined behavior even if the translation
	     produced a string with the wrong number of "%s"s.  */
	  char *yyp = yyresult;
	  int yyi = 0;
	  while ((*yyp = *yyf) != '\0')
	    {
	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
		{
		  yyp += yytnamerr (yyp, yyarg[yyi++]);
		  yyf += 2;
		}
	      else
		{
		  yyp++;
		  yyf++;
		}
	    }
	}
      return yysize;
    }
}
#endif /* YYERROR_VERBOSE */


/*-----------------------------------------------.
| Release the memory associated to this symbol.  |
`-----------------------------------------------*/

/*ARGSUSED*/
#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
static void
yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
#else
static void
yydestruct (yymsg, yytype, yyvaluep)
    const char *yymsg;
    int yytype;
    YYSTYPE *yyvaluep;
#endif
{
  YYUSE (yyvaluep);

  if (!yymsg)
    yymsg = "Deleting";
  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);

  switch (yytype)
    {

      default:
	break;
    }
}


/* Prevent warnings from -Wmissing-prototypes.  */

#ifdef YYPARSE_PARAM
#if defined __STDC__ || defined __cplusplus
int yyparse (void *YYPARSE_PARAM);
#else
int yyparse ();
#endif
#else /* ! YYPARSE_PARAM */
#if defined __STDC__ || defined __cplusplus
int yyparse (void);
#else
int yyparse ();
#endif
#endif /* ! YYPARSE_PARAM */



/* The look-ahead symbol.  */
int yychar;

/* The semantic value of the look-ahead symbol.  */
YYSTYPE yylval;

/* Number of syntax errors so far.  */
int yynerrs;



/*----------.
| yyparse.  |
`----------*/

#ifdef YYPARSE_PARAM
#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
int
yyparse (void *YYPARSE_PARAM)
#else
int
yyparse (YYPARSE_PARAM)
    void *YYPARSE_PARAM;
#endif
#else /* ! YYPARSE_PARAM */
#if (defined __STDC__ || defined __C99__FUNC__ \
     || defined __cplusplus || defined _MSC_VER)
int
yyparse (void)
#else
int
yyparse ()

#endif
#endif
{
  
  int yystate;
  int yyn;
  int yyresult;
  /* Number of tokens to shift before error messages enabled.  */
  int yyerrstatus;
  /* Look-ahead token as an internal (translated) token number.  */
  int yytoken = 0;
#if YYERROR_VERBOSE
  /* Buffer for error messages, and its allocated size.  */
  char yymsgbuf[128];
  char *yymsg = yymsgbuf;
  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
#endif

  /* Three stacks and their tools:
     `yyss': related to states,
     `yyvs': related to semantic values,
     `yyls': related to locations.

     Refer to the stacks thru separate pointers, to allow yyoverflow
     to reallocate them elsewhere.  */

  /* The state stack.  */
  yytype_int16 yyssa[YYINITDEPTH];
  yytype_int16 *yyss = yyssa;
  yytype_int16 *yyssp;

  /* The semantic value stack.  */
  YYSTYPE yyvsa[YYINITDEPTH];
  YYSTYPE *yyvs = yyvsa;
  YYSTYPE *yyvsp;



#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))

  YYSIZE_T yystacksize = YYINITDEPTH;

  /* The variables used to return semantic value and location from the
     action routines.  */
  YYSTYPE yyval;


  /* The number of symbols on the RHS of the reduced rule.
     Keep to zero when no symbol should be popped.  */
  int yylen = 0;

  YYDPRINTF ((stderr, "Starting parse\n"));

  yystate = 0;
  yyerrstatus = 0;
  yynerrs = 0;
  yychar = YYEMPTY;		/* Cause a token to be read.  */

  /* Initialize stack pointers.
     Waste one element of value and location stack
     so that they stay on the same level as the state stack.
     The wasted elements are never initialized.  */

  yyssp = yyss;
  yyvsp = yyvs;

  goto yysetstate;

/*------------------------------------------------------------.
| yynewstate -- Push a new state, which is found in yystate.  |
`------------------------------------------------------------*/
 yynewstate:
  /* In all cases, when you get here, the value and location stacks
     have just been pushed.  So pushing a state here evens the stacks.  */
  yyssp++;

 yysetstate:
  *yyssp = yystate;

  if (yyss + yystacksize - 1 <= yyssp)
    {
      /* Get the current used size of the three stacks, in elements.  */
      YYSIZE_T yysize = yyssp - yyss + 1;

#ifdef yyoverflow
      {
	/* Give user a chance to reallocate the stack.  Use copies of
	   these so that the &'s don't force the real ones into
	   memory.  */
	YYSTYPE *yyvs1 = yyvs;
	yytype_int16 *yyss1 = yyss;


	/* Each stack pointer address is followed by the size of the
	   data in use in that stack, in bytes.  This used to be a
	   conditional around just the two extra args, but that might
	   be undefined if yyoverflow is a macro.  */
	yyoverflow (YY_("memory exhausted"),
		    &yyss1, yysize * sizeof (*yyssp),
		    &yyvs1, yysize * sizeof (*yyvsp),

		    &yystacksize);

	yyss = yyss1;
	yyvs = yyvs1;
      }
#else /* no yyoverflow */
# ifndef YYSTACK_RELOCATE
      goto yyexhaustedlab;
# else
      /* Extend the stack our own way.  */
      if (YYMAXDEPTH <= yystacksize)
	goto yyexhaustedlab;
      yystacksize *= 2;
      if (YYMAXDEPTH < yystacksize)
	yystacksize = YYMAXDEPTH;

      {
	yytype_int16 *yyss1 = yyss;
	union yyalloc *yyptr =
	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
	if (! yyptr)
	  goto yyexhaustedlab;
	YYSTACK_RELOCATE (yyss);
	YYSTACK_RELOCATE (yyvs);

#  undef YYSTACK_RELOCATE
	if (yyss1 != yyssa)
	  YYSTACK_FREE (yyss1);
      }
# endif
#endif /* no yyoverflow */

      yyssp = yyss + yysize - 1;
      yyvsp = yyvs + yysize - 1;


      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
		  (unsigned long int) yystacksize));

      if (yyss + yystacksize - 1 <= yyssp)
	YYABORT;
    }

  YYDPRINTF ((stderr, "Entering state %d\n", yystate));

  goto yybackup;

/*-----------.
| yybackup.  |
`-----------*/
yybackup:

  /* Do appropriate processing given the current state.  Read a
     look-ahead token if we need one and don't already have one.  */

  /* First try to decide what to do without reference to look-ahead token.  */
  yyn = yypact[yystate];
  if (yyn == YYPACT_NINF)
    goto yydefault;

  /* Not known => get a look-ahead token if don't already have one.  */

  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
  if (yychar == YYEMPTY)
    {
      YYDPRINTF ((stderr, "Reading a token: "));
      yychar = YYLEX;
    }

  if (yychar <= YYEOF)
    {
      yychar = yytoken = YYEOF;
      YYDPRINTF ((stderr, "Now at end of input.\n"));
    }
  else
    {
      yytoken = YYTRANSLATE (yychar);
      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
    }

  /* If the proper action on seeing token YYTOKEN is to reduce or to
     detect an error, take that action.  */
  yyn += yytoken;
  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
    goto yydefault;
  yyn = yytable[yyn];
  if (yyn <= 0)
    {
      if (yyn == 0 || yyn == YYTABLE_NINF)
	goto yyerrlab;
      yyn = -yyn;
      goto yyreduce;
    }

  if (yyn == YYFINAL)
    YYACCEPT;

  /* Count tokens shifted since error; after three, turn off error
     status.  */
  if (yyerrstatus)
    yyerrstatus--;

  /* Shift the look-ahead token.  */
  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);

  /* Discard the shifted token unless it is eof.  */
  if (yychar != YYEOF)
    yychar = YYEMPTY;

  yystate = yyn;
  *++yyvsp = yylval;

  goto yynewstate;


/*-----------------------------------------------------------.
| yydefault -- do the default action for the current state.  |
`-----------------------------------------------------------*/
yydefault:
  yyn = yydefact[yystate];
  if (yyn == 0)
    goto yyerrlab;
  goto yyreduce;


/*-----------------------------.
| yyreduce -- Do a reduction.  |
`-----------------------------*/
yyreduce:
  /* yyn is the number of a rule to reduce with.  */
  yylen = yyr2[yyn];

  /* If YYLEN is nonzero, implement the default value of the action:
     `$$ = $1'.

     Otherwise, the following line sets YYVAL to garbage.
     This behavior is undocumented and Bison
     users should not rely upon it.  Assigning to YYVAL
     unconditionally makes the parser a bit smaller, and it avoids a
     GCC warning that YYVAL may be used uninitialized.  */
  yyval = yyvsp[1-yylen];


  YY_REDUCE_PRINT (yyn);
  switch (yyn)
    {
        case 2:

    { (yyval.ival) = 0;;}
    break;

  case 3:

    {(yyval.ival) = 0;
	   if (strcmp((yyvsp[(1) - (1)].pval)->lhs, "c"))
	     productions.pushBack((yyvsp[(1) - (1)].pval));
           else
	     recordToken(strdup("C"));;}
    break;

  case 4:

    {(yyval.ival) = 0;
	   if (strcmp((yyvsp[(2) - (2)].pval)->lhs, "c"))
	     productions.pushBack((yyvsp[(2) - (2)].pval));
	   else
	     recordToken(strdup("C"));;}
    break;

  case 5:

    {(yyval.pval) = new production((yyvsp[(1) - (3)].sval), (yyvsp[(2) - (3)].ldvl));
	   (yyval.pval)->endsInOptional = findEndsInOptional((yyval.pval));;}
    break;

  case 6:

    { (yyval.sval) = (yyvsp[(1) - (2)].sval); ;}
    break;

  case 7:

    { (yyval.sval) = (yyvsp[(1) - (2)].sval); ;}
    break;

  case 8:

    { (yyval.sval) = (yyvsp[(1) - (2)].sval); ;}
    break;

  case 9:

    {(yyval.ldvl) = new defList((yyvsp[(1) - (1)].fval));;}
    break;

  case 10:

    {(yyval.ldvl) = (yyvsp[(1) - (3)].ldvl);
	   (yyvsp[(1) - (3)].ldvl)->pushBack((yyvsp[(3) - (3)].fval));;}
    break;

  case 11:

    {(yyval.fval) = new definition(0, (yyvsp[(1) - (1)].levl), 0); ;}
    break;

  case 12:

    {(yyval.fval) = new definition((yyvsp[(2) - (2)].sval), (yyvsp[(1) - (2)].levl), 0); ;}
    break;

  case 13:

    {(yyval.levl) = new expList();;}
    break;

  case 14:

    {(yyval.levl) = new expList((yyvsp[(1) - (1)].exal));;}
    break;

  case 15:

    {(yyval.levl) = (yyvsp[(1) - (3)].levl);
	   (yyvsp[(1) - (3)].levl)->pushBack((yyvsp[(3) - (3)].exal));;}
    break;

  case 16:

    {(yyval.exal) = new expression(KEYWORD, (yyvsp[(1) - (1)].sval), 0, 0, 0);
	   recordToken((yyvsp[(1) - (1)].sval)); ;}
    break;

  case 17:

    {(yyval.exal) = new expression(NONTERMINAL, (yyvsp[(1) - (2)].sval), (yyvsp[(2) - (2)].sval), 0, 0);;}
    break;

  case 18:

    {if (strcmp((yyvsp[(1) - (1)].sval), "c"))
	      (yyval.exal) = new expression(NONTERMINAL, (yyvsp[(1) - (1)].sval), 0, 0, 0);
	   else
	     (yyval.exal) = commaExp;;}
    break;

  case 19:

    {(yyval.exal) = new expression(TERMINAL, (yyvsp[(1) - (2)].sval), (yyvsp[(2) - (2)].sval), 0, 0);
	   recordTerminal((yyvsp[(1) - (2)].sval));;}
    break;

  case 20:

    {(yyval.exal) = new expression(TERMINAL, (yyvsp[(1) - (1)].sval), 0, 0, 0);
	   recordTerminal((yyvsp[(1) - (1)].sval));;}
    break;

  case 21:

    {(yyval.exal) = new expression(TERMINALSTRING, (yyvsp[(1) - (1)].sval), 0, 0, 0);;}
    break;

  case 22:

    {(yyval.exal) = new expression(ONECHAR, (yyvsp[(1) - (1)].sval), 0, 0, 0);;}
    break;

  case 23:

    {(yyval.exal) = new expression(TWOCHAR, (yyvsp[(1) - (1)].sval), 0, 0, 0);;}
    break;

  case 24:

    {(yyval.exal) = new expression(OPTIONAL, 0, 0, (yyvsp[(1) - (1)].oval), 0);;}
    break;

  case 25:

    {(yyval.exal) = new expression(ENDLINE, strdup("ENDLINE"), 0, 0, 0);;}
    break;

  case 26:

    {(yyval.oval) = new optional((yyvsp[(2) - (3)].levl), 1);;}
    break;

  case 27:

    {(yyval.oval) = new optional((yyvsp[(4) - (5)].levl), (yyvsp[(1) - (5)].ival));;}
    break;


/* Line 1267 of yacc.c.  */

      default: break;
    }
  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);

  YYPOPSTACK (yylen);
  yylen = 0;
  YY_STACK_PRINT (yyss, yyssp);

  *++yyvsp = yyval;


  /* Now `shift' the result of the reduction.  Determine what state
     that goes to, based on the state we popped back to and the rule
     number reduced by.  */

  yyn = yyr1[yyn];

  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
    yystate = yytable[yystate];
  else
    yystate = yydefgoto[yyn - YYNTOKENS];

  goto yynewstate;


/*------------------------------------.
| yyerrlab -- here on detecting error |
`------------------------------------*/
yyerrlab:
  /* If not already recovering from an error, report this error.  */
  if (!yyerrstatus)
    {
      ++yynerrs;
#if ! YYERROR_VERBOSE
      yyerror (YY_("syntax error"));
#else
      {
	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
	  {
	    YYSIZE_T yyalloc = 2 * yysize;
	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
	    if (yymsg != yymsgbuf)
	      YYSTACK_FREE (yymsg);
	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
	    if (yymsg)
	      yymsg_alloc = yyalloc;
	    else
	      {
		yymsg = yymsgbuf;
		yymsg_alloc = sizeof yymsgbuf;
	      }
	  }

	if (0 < yysize && yysize <= yymsg_alloc)
	  {
	    (void) yysyntax_error (yymsg, yystate, yychar);
	    yyerror (yymsg);
	  }
	else
	  {
	    yyerror (YY_("syntax error"));
	    if (yysize != 0)
	      goto yyexhaustedlab;
	  }
      }
#endif
    }



  if (yyerrstatus == 3)
    {
      /* If just tried and failed to reuse look-ahead token after an
	 error, discard it.  */

      if (yychar <= YYEOF)
	{
	  /* Return failure if at end of input.  */
	  if (yychar == YYEOF)
	    YYABORT;
	}
      else
	{
	  yydestruct ("Error: discarding",
		      yytoken, &yylval);
	  yychar = YYEMPTY;
	}
    }

  /* Else will try to reuse look-ahead token after shifting the error
     token.  */
  goto yyerrlab1;


/*---------------------------------------------------.
| yyerrorlab -- error raised explicitly by YYERROR.  |
`---------------------------------------------------*/
yyerrorlab:

  /* Pacify compilers like GCC when the user code never invokes
     YYERROR and the label yyerrorlab therefore never appears in user
     code.  */
  if (/*CONSTCOND*/ 0)
     goto yyerrorlab;

  /* Do not reclaim the symbols of the rule which action triggered
     this YYERROR.  */
  YYPOPSTACK (yylen);
  yylen = 0;
  YY_STACK_PRINT (yyss, yyssp);
  yystate = *yyssp;
  goto yyerrlab1;


/*-------------------------------------------------------------.
| yyerrlab1 -- common code for both syntax error and YYERROR.  |
`-------------------------------------------------------------*/
yyerrlab1:
  yyerrstatus = 3;	/* Each real token shifted decrements this.  */

  for (;;)
    {
      yyn = yypact[yystate];
      if (yyn != YYPACT_NINF)
	{
	  yyn += YYTERROR;
	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
	    {
	      yyn = yytable[yyn];
	      if (0 < yyn)
		break;
	    }
	}

      /* Pop the current state because it cannot handle the error token.  */
      if (yyssp == yyss)
	YYABORT;


      yydestruct ("Error: popping",
		  yystos[yystate], yyvsp);
      YYPOPSTACK (1);
      yystate = *yyssp;
      YY_STACK_PRINT (yyss, yyssp);
    }

  if (yyn == YYFINAL)
    YYACCEPT;

  *++yyvsp = yylval;


  /* Shift the error token.  */
  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);

  yystate = yyn;
  goto yynewstate;


/*-------------------------------------.
| yyacceptlab -- YYACCEPT comes here.  |
`-------------------------------------*/
yyacceptlab:
  yyresult = 0;
  goto yyreturn;

/*-----------------------------------.
| yyabortlab -- YYABORT comes here.  |
`-----------------------------------*/
yyabortlab:
  yyresult = 1;
  goto yyreturn;

#ifndef yyoverflow
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here.  |
`-------------------------------------------------*/
yyexhaustedlab:
  yyerror (YY_("memory exhausted"));
  yyresult = 2;
  /* Fall through.  */
#endif

yyreturn:
  if (yychar != YYEOF && yychar != YYEMPTY)
     yydestruct ("Cleanup: discarding lookahead",
		 yytoken, &yylval);
  /* Do not reclaim the symbols of the rule which action triggered
     this YYABORT or YYACCEPT.  */
  YYPOPSTACK (yylen);
  YY_STACK_PRINT (yyss, yyssp);
  while (yyssp != yyss)
    {
      yydestruct ("Cleanup: popping",
		  yystos[*yyssp], yyvsp);
      YYPOPSTACK (1);
    }
#ifndef yyoverflow
  if (yyss != yyssa)
    YYSTACK_FREE (yyss);
#endif
#if YYERROR_VERBOSE
  if (yymsg != yymsgbuf)
    YYSTACK_FREE (yymsg);
#endif
  /* Make sure YYID is used.  */
  return YYID (yyresult);
}





/********************************************************************/

/* addData

Returned Value: none

Called By: main

This goes through all the productions and, for each one, first
calls findSupertypes and then calls findClassNames. 

findSupertypes must be called first because findClassNames tests 
the value of isSupertype.

*/

void addData() /* NO ARGUMENTS */
{
  prodListCell * prodCell;
  production * prod;

  for (prodCell = productions.first; prodCell; prodCell = prodCell->next)
    {
      prod = prodCell->data;
      findSupertypes(prod);
      findClassNames(prod);
    }
}

/********************************************************************/

/* alwaysFollowedByComma

Returned Value: bool

This returns true if alwaysFollowedByCommaInExps returns true for a
given production prod and every list of expressions in every
definition of every production that uses prod.  Otherwise, it returns
false.

Called By:
  fixConflictListNested
  fixConflicts1

See documentation of alwaysFollowedByCommaInExps, also.

*/

bool alwaysFollowedByComma( /* ARGUMENTS           */
 production * prod)         /* production to check */
{
  prodListCell * prodCell;
  defListCell * defCell;

  for (prodCell = prod->usedIn.first; prodCell; prodCell = prodCell->next)
    {
      for (defCell = prodCell->data->defs->first;
	   defCell;
	   defCell = defCell->next)
	{
	  if (alwaysFollowedByCommaInExps
	      (prod, defCell->data->expressions) == false)
	    return false;
	}
    }
  return true;
}

/********************************************************************/

/* alwaysFollowedByCommaInExps

Returned Value: bool

Called By:
  alwaysFollowedByComma
  alwaysFollowedByCommaInExps (recursively)

If prod is always followed immediately by an expression that is a
comma wherever it appears in a list of expressions (including inside
an optional that is one of the expressions), this returns true.
Otherwise, it returns false. This calls itself recursively to dig
down into nests of optionals.

A list of expressions of the form "prod, [c, bleep], c," will cause
this function to return false even though prod will be followed by a
comma whether or not the optional is used. But we don't care about
false negatives since there are no cases in DMIS where that fix is
needed.

*/

bool alwaysFollowedByCommaInExps( /* ARGUMENTS                    */
 production * prod,               /* production to check          */
 expList * exps)                  /* list of expressions to check */
{
  expListCell * expCell;

  for (expCell = exps->first; expCell; expCell = expCell->next)
    {
      if (expIsProd(expCell, prod) == true)
	{ 
	  if ((expCell->next == 0) || (expIsComma(expCell->next) == false))
	    return false; // prod found not followed by comma
	}
      else if (expCell->data->theType == OPTIONAL)
	{
	  if (alwaysFollowedByCommaInExps
	      (prod, expCell->data->optValue->expressions) == false)
	    return false;
	}
    }
  return true;
}

/********************************************************************/

/* alwaysFollowedByNewline

Returned Value: bool
If the given production is determined to always be followed by a
newline within the maximum allowed recursions, this returns true.
Otherwise, it returns false.

Called By:
  alwaysFollowedByNewline (recursively)
  fixConflictListNested
  fixConflictOther

This determines if an occurrence of a production will always be
followed by a newline in a statement. For this to happen, in every
definition that uses prod in every other production that uses prod, either
1. prod must occur as the next-to-last expression and the last must
   be a newline, or
2. prod must occur as the last expression, and the user production
   must always be followed by a newline.

Test two requires that this function call itself recursively.
This limits the recursion of the function so that a production with
a recursive definition does not cause the function recurse endlessly.

An (abbreviated) example of how a production may be followed by a
newline follows. In the example, tolCprofEnd is always followed by a
newline because it appears only as the last expression in a definition
of tolCprofs, which appears only as the last expression in a
definition of tolMinor, which appears only as the next to last
expression in a definitions of tolStm, where it is followed by a
newline.

tolStm = tLabel, '=', TOL, '/', tolMinor, # ;

tolMinor = tolCprofs ;

tolCprofs = CPROFS, c, rentVal, c, rentVal, c, tolCprofEnd ;

tolCprofEnd = 3*[tolFeatureMat, c], [AVGDEV, c], rentVal

*/

bool alwaysFollowedByNewline( /* ARGUMENTS           */
 production * prod,           /* production to check */
 int depth)                   /* maximum recursion   */
{
  prodListCell * prodCell;
  defListCell * defCell;
  expListCell * expCell;

  if (depth == 0)
    return false;
  for (prodCell = prod->usedIn.first; prodCell; prodCell = prodCell->next)
    {
      defCell = prodCell->data->defs->first;
      for ( ; defCell; defCell = defCell->next)
	{
	  expCell = defCell->data->expressions->first;
	  for ( ; expCell; expCell = expCell->next)
	    {
	      if (expIsProd(expCell, prod))
		{ // found prod
		  if (expCell->next)
		    { // prod is not last
		      if (expCell->next->next)
			{ // prod occurs before next-to-last
			  return false;
			}
		      else if (expIsNewline(expCell->next) == false)
			{ // prod is next to last, but last is not newline
			  return false;
			}
		    }
		  else
		    { // prod is last
		      if (alwaysFollowedByNewline(prodCell->data, (depth-1))
			  == false)
			return false;
		    }
		}
	    }
	}
    }
  return true;
}

/********************************************************************/

/* checkDuplicates

Returned Value: none

Called By: replaceOptsInDefinitions

This goes through a list of definitions, and for each definition def1,
compares all the definitions in the list after def1 with def1. If any
definition def2 after def1 is a duplicate of def1, this stops, prints
an error message, and exits.

*/

void checkDuplicates( /* ARGUMENTS            */
 defList * defs)      /* definitions to check */
{
  defListCell * defCell1;
  defListCell * defCell2;
  
  for (defCell1 = defs->first; defCell1; defCell1 = defCell1->next)
    {
      for (defCell2 = defCell1->next; defCell2; defCell2 = defCell2->next)
	{
	  if (expListsSame(defCell1->data->expressions,
			   defCell2->data->expressions))
	    {
	      if (defCell1->data->expressions->first->data == commaExp)
		fprintf(stderr,
			"duplicate empty definitions found starting with "
			"a comma\n");
	      else if (defCell1->data->expressions->first->data->itemName)
		fprintf(stderr,
			"duplicate definitions found starting with %s\n",
			defCell1->data->expressions->first->data->itemName);
	      else
		fprintf(stderr, "duplicate empty definitions found\n");
	      exit(1);
	    }
	}
    }
}

/********************************************************************/

/* expIsComma

Returned Value: bool
If the expression pointed at by the expCell is a comma, this returns true.
Otherwise, it returns false.

Called By:
  alwaysFollowedByComma
  findEndsInOptional
  fixConflictList
  fixConflictListNested
  fixConflictListNested2
  fixConflictOther
  fixConflictOther2
  removeCommaInExps
  replaceProdC

Every expression representing a comma is commaExp, so this just checks
that the expression in the expCell is commaExp.

*/

bool expIsComma(        /* ARGUMENTS                     */
 expListCell * expCell) /* expression list cell to check */
{
  if (expCell && expCell->data && (expCell->data == commaExp))
    return true;
  return false;
}

/********************************************************************/

/* expIsNewline

Returned Value: bool
If the expression pointed at by the expCell is a newline, this returns true.
Otherwise, it returns false.

Called By:
  alwaysFollowedByNewline
  fixConflictOther

The check of the itemName is probably not necessary.

*/

bool expIsNewline(      /* ARGUMENTS                     */
 expListCell * expCell) /* expression list cell to check */
{
  if (expCell && expCell->data &&
      (expCell->data->theType == ENDLINE) &&
      (strcmp(expCell->data->itemName, "ENDLINE") == 0))
    return true;
  return false;
}

/********************************************************************/

/* expIsProd

Returned Value: bool
If the expression pointed at by the expCell is prod, this returns true.
Otherwise, it returns false.

Called By:
  alwaysFollowedByCommaInExps
  alwaysFollowedByNewline
  fixConflictOther
  fixConflictOther2
  removeCommaInExps
  replaceProdC

Note: Since expression::dup uses the original prodValue, not a copy,
it is OK to check for equality to prod, even when prod is part of an
expression that has been duplicated.

This is being very fussy with its checks so as to avoid dereferencing
a NULL pointer.

*/

bool expIsProd(         /* ARGUMENTS                       */
 expListCell * expCell, /* expression list cell to compare */
 production * prod)     /* production to compare           */
{
  if (prod &&
      expCell &&
      expCell->data &&
      expCell->data->prodValue &&
      (prod == expCell->data->prodValue))
    return true;
  return false;
}

/********************************************************************/

/* expListsSame

Returned Value: int

If the exps1 and exps2 are identical lists of expressions that contain
no optionals, this returns 1. If either list contains an optional
(which should never happen and means there is a bug), this exits.
Otherwise, it returns 0.

Called By: checkDuplicates

*/

int expListsSame( /* ARGUMENTS                         */
 expList * exps1, /* first expression list to compare  */
 expList * exps2) /* second expression list to compare */
{
  expListCell * cell1;
  expListCell * cell2;
  expression * exp1;
  expression * exp2;

  for (cell1 = exps1->first, cell2 = exps2->first;
       (cell1 && cell2);
       cell1 = cell1->next, cell2 = cell2->next)
    {
      exp1 = cell1->data;
      exp2 = cell2->data;
      if ((exp1->theType == OPTIONAL) || (exp2->theType == OPTIONAL))
	{
	  fprintf(stderr, "BUG in expListsSame\n");
	  exit(1);
	}
      else if (exp1 == commaExp)
	{
	  if (exp2 == commaExp)
	    continue;
	  else
	    return 0;
	}
      else if (exp2 == commaExp)
	return 0;
      else if (exp1->theType != exp2->theType)
	return 0;
      else if (strcmp(exp1->itemName, exp2->itemName))
	return 0;
    }
  return (((cell1 == 0) && (cell2 == 0)) ? 1 : 0);
}

/********************************************************************/

/* findAttributeNames

Returned Value: none

Called By:
  printCppClassDerived
  printCppClassTop

This generates names for those expressions that do not have names
given in the debnf file and correspond to the attributes of a class
that represents the definition. Each name generated is stored in the
corresponding expression as the value of attName.

The names this generates that end in _N are always lousy names, since
they have no useful semantic meaning. It is suggested that names be
assigned explicitly in the debnf file wherever _N names would
otherwise be generated by this function.

The functions works from the flattened list of expressions. See the
documentation of flattenOpts for details of flattened expression
lists. The following rules are applied to the flattened list of
expressions.

The rules for generating an attribute name for an expression follow.
In these rules, N is the 1-based index of the expression in the exps.

1. If theType of an expression is NONTERMINAL:

1a. If the itemName of the expression appears only once in the exps,
then the attribute name is formed by joining the prefix "a_" with the
itemName. For example, a_bleep.

1b. If the itemName of the expression appears more than once in the
exps, then the attribute name is formed by joining the itemName and
the suffix "_N". For example, bleep_3.

2. If theType of an expression is TERMINAL (the C++ type for which is
int, char, or double), the attribute name is a_int, int_N, a_char,
char_N, a_double, or double_N, (where the use of N is determined
as in 1).

3. If theType of an expression is OPTIONAL (flattenOpts leaves some
things as optionals), then the optional contains at least one keyword,
and the attribute name is the first keyword in the expressions of the
optional preceded by the prefix "has_".  If the same optional keyword
appears twice in a list of expressions (which happens in DMIS), then
the suffix "_N" is added. For example, has_RAM.

For terminals and nonterminals, the approach is to look through the
flattened expressions. For each expression exp that does not already
have a name, the remaining expressions are examined to see if any have
the same itemName as exp.  If so, exp and all expressions with the same
itemName get a name made by concatenating "a_", the itemName, and "_N".
If not, exp gets a name made by concatenating "a_" and the itemName.

For terminals, the method is identical, except that substitutions are
used for the itemName (CHARSTRING->char, INTSTRING->int, REALSTRING->double).

The method for optionals is similar, but finding the equivalent of
the itemName takes a little more work, and the prefix "has_" is used
instead of "a_".

*/

void findAttributeNames( /* ARGUMENTS                                       */
 expList * flatExps)     /* flattened list of expressions from a definition */
{
  int n;
  int m;
  expListCell * expCell;
  expListCell * eCell;
  expression * exp;
  expression * e;
  bool foundDup;
  char name[NAMESIZE];
  char * expName;
  
  for (n=1, expCell = flatExps->first; expCell; n++, expCell = expCell->next)
    {
      foundDup = false;
      exp = expCell->data;
      if (exp->attName) // skip those that already have an attribute name
	continue;
      if (exp->theType == NONTERMINAL)
	{
	  expName = exp->itemName;
	  for (m=(n+1), eCell = expCell->next; eCell; m++, eCell = eCell->next)
	    {
	      e = eCell->data;
	      if ((e->theType == NONTERMINAL) &&
		  (strcmp(expName, e->itemName) == 0))
		{
		  if (foundDup == false)
		    {
		      sprintf(name, "%s_%d", expName, n);
		      exp->attName = strdup(name);
		      foundDup = true;
		    }
		  sprintf(name, "%s_%d", expName, m);
		  e->attName = strdup(name);
		}
	    }
	  if (foundDup == false)
	    {
	      sprintf(name, "a_%s", expName);
	      exp->attName = strdup(name);
	    }
	}
      else if (exp->theType == TERMINAL)
	{
	  expName = exp->itemName;
	  for (m=(n+1), eCell = expCell->next; eCell; m++, eCell = eCell->next)
	    {
	      e = eCell->data;
	      if ((e->theType == TERMINAL) &&
		  (strcmp(expName, e->itemName) == 0))
		{
		  if (foundDup == false)
		    {
		      if (strcmp(expName, "INTSTRING") == 0)
			sprintf(name, "int_%d", n);
		      else if (strcmp(expName, "REALSTRING") == 0)
			sprintf(name, "double_%d", n);
		      else // if (strcmp(expName, "CHARSTRING") == 0)
			sprintf(name, "string_%d", n);
		      exp->attName = strdup(name);
		      foundDup = true;
		    }
		  if (strcmp(expName, "INTSTRING") == 0)
		    sprintf(name, "int_%d", m);
		  else if (strcmp(expName, "REALSTRING") == 0)
		    sprintf(name, "double_%d", m);
		  else // if (strcmp(expName, "CHARSTRING") == 0)
		    sprintf(name, "string_%d", m);
		  e->attName = strdup(name);
		}
	    }
	  if (foundDup == false)
	    {
	      if (strcmp(expName, "INTSTRING") == 0)
		sprintf(name, "a_int");
	      else if (strcmp(expName, "REALSTRING") == 0)
		sprintf(name, "a_double");
	      else // if (strcmp(expName, "CHARSTRING") == 0)
		sprintf(name, "a_string");
	      exp->attName = strdup(name);
	    }
	}
      else if (exp->theType == OPTIONAL)
	{
	  expName = findKeyWord(exp->optValue->expressions);
	  for (m=(n+1), eCell = expCell->next; eCell; m++, eCell = eCell->next)
	    {
	      e = eCell->data;
	      if ((e->theType == OPTIONAL) &&
		  (strcmp(expName, findKeyWord(e->optValue->expressions)) == 0))
		{
		  if (foundDup == false)
		    {
		      sprintf(name, "has_%s_%d", expName, n);
		      exp->attName = strdup(name);
		      foundDup = true;
		    }
		  sprintf(name, "has_%s_%d", expName, m);
		  e->attName = strdup(name);
		}
	    }
	  if (foundDup == false)
	    {
	      sprintf(name, "has_%s", expName);
	      exp->attName = strdup(name);
	    }
	}
    }
}

/********************************************************************/

/* findClassData

Returned Value: classData
If a classData is found in classDatas whose name is the same as
className, that classData is returned.
Otherwise, this prints an error message and exits.

Called By:
  makeClassDataSubs
  printConfAttChecker
  printConfBlockAttChecker
  printConfBlockSubChecker
  printConfSubChecker
  printCppProductionClasses

*/

classData * findClassData( /* ARGUMENTS                              */
 char * className,         /* name of class for which to find data   */
 classData * classDatas[26][CLASSSIZE]) /* array of class data lists */
{
  classData ** letterDatas;
  int n;

  letterDatas = classDatas[className[0] - 'a'];
  for (n = 0; ((n < CLASSSIZE) && (letterDatas[n] != 0)); n++)
    {
      if (strcmp(className, letterDatas[n]->name) == 0)
	return letterDatas[n];
    }
  if (letterDatas[n] == 0)
    {
      fprintf(stderr, "No class data found for %s\n", className);
      exit(1);
    }
  return 0;
}

/********************************************************************/

/* findClassNames

Returned Value: none

Called By: addData

If every definition of prod already has its className attribute set,
this function does nothing. Otherwise, this sets the className of each
definition of prod.

The className attribute of a definition may be set by including it as
comment in the EBNF of the form (*C=className*). Since all the
classNames of the definitions of a production will be overwritten if
any definition does not have a className assigned in the EBNF file, if
any definition of a production is given a className, then all of the
definitions of the production should be given a className.

If the production is a supertype, then this sets the className of each
definition to the itemName of the one and only expression in the
definition's list of expressions (which is the same as the lhs of the
prodValue of that expression).

Otherwise, if there is only one definition, then its className is set
to the name of the production (i.e., prod->lhs).

Otherwise, this looks through the definitions of prod. For the Nth
definition (using 1-based indexing so that the first definition has
N = 1), if any of the following three conditions holds, then this makes
a name of the form prodName_itemName (the production name followed by
an underscore followed by the itemName of an expression in the
definition).

(1) every definition has exactly one expression and each of those
    expressions has an itemName (in this case, all the itemNames must
    differ), or
(2) every definition starts with a keyword or a nonterminal and no
    two definitions start with the same thing.
(3) every definition is identical, except for one term that is either
    a keyword or a nonterminal.

If none of the three conditions above holds, then this makes a name of
the form prodName_N. That is a lousy name since it does not give the user
any idea about the contents of the definition. In such cases, it is
a good idea to assign in the EBNF the className of each definition.

Inside this function, the className attribute of a definition is used
in some cases to hold part of the class name temporarily. In those
cases, the full class name is set in the last block of this function.

*/

void findClassNames( /* ARGUMENTS                               */
 production * prod)  /* production for which to find class name */
{
  int n;
  int m;
  int place;
  defListCell * defCell;
  defListCell * dCell;
  expList * exps1;
  expList * exps2;
  bool useItemName;
  char * key;
  char buffer[NAMESIZE];
  
  n = prod->defs->findLength();
  if (n == 0)
    {
      fprintf(stderr, "production %s has no definitions\n", prod->lhs);
      exit(1);
    }
  for (defCell = prod->defs->first; defCell; defCell = defCell->next)
    {
      if (!defCell->data->className)
	break;
    }
  if (!defCell) // all definitions already have names, so use them
    return;
  if (prod->isSupertype == true)
    {
      for (defCell = prod->defs->first; defCell; defCell = defCell->next)
	{
	  defCell->data->className =
	    defCell->data->expressions->first->data->itemName;
	}
      return;
    }
  else if (n == 1)
    {
      prod->defs->first->data->className = prod->lhs;
      return;
    }
  useItemName = true;
  for (n=1, defCell=prod->defs->first; defCell; n++, defCell=defCell->next)
    { // check for condition 1
      exps1 = defCell->data->expressions;
      if ((exps1->findLength() != 1) || (exps1->first->data->itemName == 0))
	{
	  useItemName = false;
	  break;
	}
      defCell->data->className = exps1->first->data->itemName;
    }
  if (useItemName == false)
    { // condition 1 does not hold; try condition 2
      useItemName = true;
      for (defCell = prod->defs->first; defCell; defCell = defCell->next)
	{
	  exps1 = defCell->data->expressions;
	  if ((exps1->first->data->theType != KEYWORD) &&
	      (exps1->first->data->theType != NONTERMINAL))
	    {
	      useItemName = false;
	      break;
	    }
	  key = exps1->first->data->itemName;
	  for (dCell=prod->defs->first; dCell != defCell; dCell=dCell->next)
	    { // Check for duplicates.
	      if (strcmp(dCell->data->className, key) == 0)
		{ // found duplicate
		  useItemName = false;
		  break;
		}
	    }
	  if (useItemName == false)
	    break;
	  defCell->data->className = key;
	}
    }
  while (useItemName == false) // really an "if"; no looping occurs
    { // conditions 1 and 2 do not hold; try condition 3
      useItemName = true;
      m = prod->defs->first->data->expressions->findLength();
      for (defCell=prod->defs->first->next; defCell; defCell=defCell->next)
	{
	  if (defCell->data->expressions->findLength() != m)
	    {
	      useItemName = false;
	      break;
	    }
	}
      if (useItemName == false)
	break; // if it goes past here, all definitions are equal in length
      exps1 = prod->defs->first->data->expressions;
      exps2 = prod->defs->first->next->data->expressions;
      place = findPlaceCheck(exps1, exps2);
      if (place == 0)
	{
	  useItemName = false;
	  break;
	} // if it goes past here, exps one and two differ only at one place
      for (defCell = prod->defs->first->next->next;
	   defCell;
	   defCell = defCell->next)
	{
	  exps2 = defCell->data->expressions;
	  m = findPlaceCheck(exps1, exps2);
	  if (m != place)
	    {
	      useItemName = false;
	      break;
	    }
	} 
      if (useItemName == false)
	break;// if it goes past here, all exps differ only at one place
      for (defCell = prod->defs->first; defCell; defCell = defCell->next)
	{
	  exps1 = defCell->data->expressions;
	  defCell->data->className = exps1->nth(place)->itemName;
	}
      break;
    }
  for (n=1, defCell=prod->defs->first; defCell; n++, defCell=defCell->next)
    {
      if (useItemName)
	sprintf(buffer, "%s_%s", prod->lhs, defCell->data->className);
      else
	sprintf(buffer, "%s_%d", prod->lhs, n);
      defCell->data->className = strdup(buffer);
    }
}

/********************************************************************/

/* findEndsInOptional

Returned Value: bool

If any definition of prod ends with an optional that starts with a
comma, this function returns true. Otherwise, it returns false.

Called By: YACC action for production

*/

bool findEndsInOptional( /* ARGUMENTS          */
 production * prod)      /* production to test */
{
  defListCell * defCell;
  expression * exp;

  for (defCell = prod->defs->first; defCell; defCell = defCell->next)
    {
      if (defCell->data->expressions->last == 0)
	continue;
      exp = defCell->data->expressions->last->data;
      if ((exp->optValue) && expIsComma(exp->optValue->expressions->first))
	return true;
    }
  return false;
}

/********************************************************************/

/* findIsBlock

Returned Value: bool

Called By: recordClasses

If the name of the production ends with "Block", this returns true.
Otherwise, it returns false.

A production is a DMIS block if and only if its name ends in "Block".

DMIS blocks are being treated specially. A block is not allowed if
and only if its first statement is not allowed. Rather than flagging
an entire block as being not allowed, which would cause the entire
block to be printed and the contents of the block to go unchecked,
we are allowing all blocks (in the xxxLists.cc files) but disallowing
the first statements of blocks that are not in the conformance class.
Printing the entire block would be confusing, and failing to check the
contents of the block would be unhelpful. The isBlock attribute of
the classData class is used to keep track of whether a class is a block.
This function makes the initial determination of whether a class is
a block.

*/

bool findIsBlock(   /* ARGUMENTS          */
 production * prod) /* production to test */
{
  int length;

  length = (strlen(prod->lhs) - 5);
  if ((length > 0) && (strcmp((prod->lhs + length), "Block") == 0)) 
    return true;
  else
    return false;
}

/********************************************************************/

/* findKeyWord

Returned Value: char *

This returns a pointer to the name of the first keyword in a list of
expressions.

Called By:  findAttributeNames

If there is no keyword in the list, this prints an error message and
exits. This function is not called unless there is a keyword among the
expressions.

*/

char * findKeyWord( /* ARGUMENTS                      */
 expList * exps)    /* list of expressions to look in */
{
  expListCell * expCell;
  for (expCell = exps->first; expCell; expCell = expCell->next)
    {
      if (expCell->data->theType == KEYWORD)
	return expCell->data->itemName;
    }
  fprintf(stderr, "Bug in findKeyWord\n");
  exit(1);
}

/********************************************************************/

/* findOptType

Returned Value: int

Called By:
  flattenOpts
  prepareDefForPlain
  printCppClassPrinterOpt

1. If the optional includes at least one terminal or nonterminal other
than a comma, this returns 1.
Example: [MAJOR, c, intVal]

2. If an optional includes no terminals or nonterminals (i.e, it
consists entirely of optionals, keywords, oneChars, twoChars, and
terminalStrings), then:

2A. If the optional (i) has digit = 1 (ii) includes no optionals and
(iii) includes at least one keyword that is not a comma, then this
returns 2.
Example:  [c, MAJOR, c, MINOR]

2B. Otherwise, this returns 0.
Example i:   2*[MAJOR]
Example ii:  [MAJOR, c, [MINOR]]
Example iii: ['t', c]

*/

int findOptType( /* ARGUMENTS        */
 optional * opt) /* optional to test */
{
  int expType;
  expListCell * expCell;
  bool hasKeyword;
  bool hasOptional;
    
  hasKeyword = false;
  hasOptional = false;
  for (expCell = opt->expressions->first; expCell; expCell = expCell->next)
    {
      expType = expCell->data->theType;
      if ((expType == TERMINAL) || (expType == NONTERMINAL))
	return 1;
      else if (expType == OPTIONAL)
	hasOptional = true;
      else if (expType == KEYWORD)
	hasKeyword = true;
    }
  if ((hasKeyword == false) || (hasOptional == true) || (opt->digit != 1))
    return 0;
  return 2;
}

/********************************************************************/

/* findPlaceCheck

Returned Value:

Called By: findClassNames

This checks that two expLists (known to be the same length) contain
only keywords, commas, onechars, and nonterminals and are identical
except at one expression which is either a keyword or nonterminal. If
so, this returns the 1-based index of the position at which they
differ. Otherwise, this returns 0.

Since this is not checking optionals (which can be structurally the
same but are never identical pointers), expressions that contain
optionals always cause this to return 0.

*/

int findPlaceCheck( /* ARGUMENTS                       */
 expList * exps1,   /* first expression list to check  */
 expList * exps2)   /* second expression list to check */
{
  expListCell * cell1;
  expListCell * cell2;
  expression * exp1;
  expression * exp2;
  int n;

  for (n = 1, cell1 = exps1->first, cell2 = exps2->first;
       cell1;
       n++, cell1 = cell1->next, cell2 = cell2->next)
    { // find the first place at which the two expLists differ
      exp1 = cell1->data;
      exp2 = cell2->data;
      if (exp1 == commaExp)
	{
	  if (exp2 != commaExp)
	    return 0;
	}
      else if (exp1->theType == ONECHAR)
	{
	  if ((exp2->theType != ONECHAR) ||
	      (strcmp(exp1->itemName, exp2->itemName)))
	    return 0;
	}
      else if (exp1->theType == KEYWORD)
	{
	  if (exp2->theType == KEYWORD)
	    {
	      if (strcmp(exp1->itemName, exp2->itemName))
		break;
	    }
	  else if (exp2->theType == NONTERMINAL)
	    break;
	  else
	    return 0;
	}
      else if (exp1->theType == NONTERMINAL)
	{
	  if (exp2->theType == NONTERMINAL)
	    {
	      if (strcmp(exp1->itemName, exp2->itemName))
		break;
	    }
	  else if (exp2->theType == KEYWORD)
	    break;
	  else
	    return 0;
	}
      else // not a comma, keyword, or nonterminal
	return 0;
    }
  if (cell1 == 0) // they are identical (should never happen)
    return 0;
  for (cell1 = cell1->next, cell2 = cell2->next;
       cell1;
       cell1 = cell1->next, cell2 = cell2->next)
    { // now make sure the rest are identical
      exp1 = cell1->data;
      exp2 = cell2->data;
      if (exp1 == commaExp)
	{
	  if (exp2 != commaExp)
	    return 0;
	}
      else if (exp1->theType == ONECHAR)
	{
	  if ((exp2->theType != ONECHAR) ||
	      (strcmp(exp1->itemName, exp2->itemName)))
	    return 0;
	}
      else if (exp1->theType == KEYWORD)
	{
	  if ((exp2->theType != KEYWORD) ||
	      (strcmp(exp1->itemName, exp2->itemName)))
	    return 0;
	}
      else if (exp1->theType == NONTERMINAL)
	{
	  if ((exp2->theType != NONTERMINAL) ||
	      (strcmp(exp1->itemName, exp2->itemName)))
	    return 0;
	}
      else // not a comma, keyword, or nonterminal
	return 0;
    }
  return n;
}

/********************************************************************/

/* findProd

Returned Value: production *
If a production with the given itemName exists in the productions list,
this returns that production. Otherwise, it returns NULL.

Called By:
  printYaccForNewDefs
  printYaccForPlain
  printYaccProductions
  printYaccProductionsStart

*/

production * findProd(  /* ARGUMENTS                  */
 const char * itemName) /* name of production to find */
{
  prodListCell * prodCell;

  for (prodCell = productions.first; prodCell; prodCell = prodCell->next)
    {
      if (strcmp(itemName, prodCell->data->lhs) == 0)
	{
	  return prodCell->data;
	}
    }
  return 0;
}

/********************************************************************/

/* findProdSet

Returned Value: none

Called By:
  insertItemAndCommaInList
  insertItemInList
  makeProdC

This finds all the productions in the defs (a list of definitions) and
puts them into a prodList named prodSet, without including duplicates.
It starts by making a copy of the defs and getting rid of optionals by
calling replaceOptsInDefinitions on the copy. Then it checks all the
expressions of all the definitions and collects those that are
productions.

*/

prodList * findProdSet( /* ARGUMENTS                                     */
 defList * defs)        /* definitions from which to extract productions */
{
  defListCell * defCell;
  expListCell * expCell;
  production * prod;
  defList * defins;
  prodList * prodSet;

  prodSet = new prodList;
  defins = defs->dup();
  replaceOptsInDefinitions(defins);
  for (defCell = defins->first; defCell; defCell = defCell->next)
    {
      expCell = defCell->data->expressions->first;
      for ( ; expCell; expCell = expCell->next)
	{
	  if (expCell->data->theType == NONTERMINAL)
	    {
	      prod = expCell->data->prodValue;
	      if (!prod)
		{
		  fprintf(stderr, "Bug in findProdSet\n");
		  exit(1);
		}
	      else
		{ // add prod to prodSet if missing
		  if (prodSet->member(prod) == 0)
		    prodSet->pushBack(prod);
		}
	    }
	}
    }
  delete defins;
  return prodSet;
}

/********************************************************************/

/* findSupertypes

Returned Value: none

Called By:  addData

If every definition of production prod has only one expression, and
that expression is a production that is not a list, then this changes
the isSupertype of prod to true and adds prod to the subtypeOf list of
all the productions in the definitions.

If a production has isSupertype=true, then a class will be generated
for it which has derived classes. If a production has isSupertype
set to false but has multiple definitions, then it will also have
derived classes.  Thus, isSupertype=true is not conceptually identical
to "has derived classes".

*/

void findSupertypes( /* ARGUMENTS                           */
 production * prod)  /* production which may be a supertype */
{
  defListCell * defCell;
  expression * exp;
  production * prod2;

  defCell = prod->defs->first;
  for ( ; defCell; defCell = defCell->next)
    {
      if (defCell->data->expressions->findLength() != 1)
	return;
      exp = defCell->data->expressions->first->data;
      if ((exp->theType != NONTERMINAL) ||
	  (exp->prodValue == 0)      ||
	  (exp->prodValue->isList))
	return;
    }
  prod->isSupertype = true;
  for (defCell = prod->defs->first; defCell; defCell = defCell->next)
    {
      prod2 = defCell->data->expressions->first->data->prodValue;
      prod2->subtypeOf.pushBack(prod);
    }
}

/********************************************************************/

/* findToken

Returned Value: int
If the text is the name of a token, this returns 1. Otherwise, it returns 0.

Called By:
  printCppClassPrinter
  printCppClassPrinterOpt1
  printCppClassPrinterOpt2
  printYaccProductions
  printYaccUnionAndTypes
  reviseSpelling
  selectProductions

If the token is found in the tokenNames, this also sets *n to the
zero-based index of the position at which the token was found in the
sub-array of the tokenNames starting with the first letter of the
token.

*/

int findToken( /* ARGUMENTS                         */
 char * text,  /* text to look for                  */
 int * n)      /* position at which found, if found */
{
  char ** letterNames;
  int result;

  if ((text[0] < 'A') || (text[0] > 'Z'))
    return 0;
  letterNames = tokenNames[text[0] - 'A'];
  for (*n = 0; (*n < LETTERSIZE); *n = (*n + 1))
    {
      if (letterNames[*n] == 0)
	return 0;
      result = strcmp(text, letterNames[*n]);
      if (result == 0) /* found it */
	return 1;
      else if (result < 0) /* text would come before letterNames[*n] */
	return 0;
    }
  return 0;
}

/********************************************************************/

/* fixConflictList

Returned Value: none

Called By: fixConflicts2

This changes productions involving lists that will cause shift/reduce
conflicts if translated to YACC with the same structure. It looks to
see if its argument, testList (known to be a list), is nested inside
another list, outerList. If so, it makes further checks and (possibly)
makes a fix. If not, it checks whether the list item for testList
(testItem) has an optional at the end starting with a comma and if so,
makes a fix.

This is called if the following are all true.
1. testList is a list.
2. testList uses a comma in the recursion (lists that don't use commas
   in the recursion don't have shift/reduce problems)

Thus, the testList argument has the following EBNF form.

testList = [testList, c], testItem ;

If there is an outerList using testList in its list item (outerItem),
a check is made that there no other list in outerItem (since this
function cannot handle more than one list in outerItem). The function
proceeds only if there is exactly one.

This function also cannot handle cases in which testList is used in
the list item of more than one outer list. The function proceeds only
if there is exactly one.

This function also cannot handle nests of lists three deep or more.
So, if there is an outerList, the function checks whether testItem
contains a production that is a list, and it checks whether any
production that uses the outerList is a list.  If either of these
occurs, no modifications are made.

Presumably, if an EBNF file contains the constructs just described
that fixConflictList cannot handle, some sort of train wreck will
occur when the YACC that results from leaving the constructs unchanged
is processed by bison. The EBNF for DMIS does not contain such
constructs.

If there is exactly one outerList
---------------------------------

Leaving the productions unmodified will result in a shift/reduce
conflict, so this calls fixConflictListNested.

If there is no outerList
------------------------

If one or more of the definitions of testItem has an optional at the
end starting with a comma, and the optional does not contain a list,
this calls fixConflictListOptional. If the optional contains a list,
both the testList and the list in the optional will be modified when
the list in the optional is processed.

*/

void fixConflictList(   /* ARGUMENTS                                   */
 production * testList) /* list that may cause a shift/reduce conflict */
{
  production * testItem;
  production * outerList;
  production * outerItem;
  production * outestList;
  defListCell * defCell;
  expListCell * expCell;
  expression * exp;
  int numberOuter;

  testItem =
    testList->defs->first->data->expressions->last->data->prodValue;
  if (testItem == 0)
    {
      fprintf(stderr, "Bug1 - prodValue missing in fixConflictList\n");
      exit(1);
    }
  if (testItem->findInnerList() != 0) // testItem uses a list, so give up
    return;
  numberOuter = testList->findOuterList(&outerList);
  if (numberOuter == 1)
    { // testList has exactly one outer list
      if (outerList->findOuterList(&outestList) != 0)
	{ // the outerItem has an outer list, so give up
	  return;
	}
      outerItem =
	outerList->defs->first->data->expressions->last->data->prodValue;
      if (outerItem == 0)
	{
	  fprintf(stderr, "Bug2 - prodValue missing in fixConflictList\n");
	  exit(1);
	}
      fixConflictListNested(outerList, outerItem, testList, testItem);
    }
  else if (numberOuter == 0)
    {// there is no outer list
      for (defCell = testItem->defs->first; defCell; defCell = defCell->next)
	{
	  exp = defCell->data->expressions->last->data;
	  if ((exp->optValue) &&  // last expression in definition is optional
	      (expIsComma(exp->optValue->expressions->first) == true))
	    break; // first expression in optional is a comma
	}
      if (defCell)
	{
	  expCell = exp->optValue->expressions->first;
	  for ( ; expCell; expCell = expCell->next)
	    {
	      if (expCell->data->prodValue &&
		  (expCell->data->prodValue->isList))
		break;
	    }
	  if (expCell == 0)
	    fixConflictListOptional(testList, testItem);
	}
    }
}

/********************************************************************/

/* fixConflictListNested

Returned Value: none

Called By: fixConflictList

When this is called, the innerList is used in at least one definition
of the outerItem, so that the innerList is nested in the outerList.
Leaving the productions unmodified may result in a shift/reduce
conflict, so modifications are made as described next. Both outerList
and innerList may be changed.

1. If 
  i. outerItem has only one definition, and
  ii. innerList appears somewhere in the middle of the flattened definition
     of outerItem, and
  ii. innerList is neither preceded by a comma nor followed by a comma
      where it appears in the flattened definition,
then there will not be a conflict, so no change is made -- for example in
the following case:

  declVarList =
	  [declVarList, c], declVar
  declVar =
	  DeclVarname, ['[', declIndicesList, ']']

2. If outerList always appears eventually before a newline in every
definition of every production that uses outerList:

a. If at least one definition of innerItem ends with an optional
starting with a comma, call fixConflictListNested1.

b. Otherwise call fixConflictListOptional since the changes required
in this list case are the same as those required when an optional
is causing a conflict.

3. Otherwise (outerList does not always eventually appear before a
newline in every definition of every production that uses outerList),
if outerList is always followed immediately by a comma wherever it
appears in every definition of every production that uses outerList,
call fixConflictListNested2.

*/

void fixConflictListNested( /* ARGUMENTS                               */
 production * outerList,    /* list built using outerItem              */
 production * outerItem,    /* item with a definition using innerList  */
 production * innerList,    /* list built using innerItem              */
 production * innerItem)    /* item from which innerList is built      */
{
  expList * flatExps;
  expListCell * expCell;

  flatExps = new expList;
  flattenOpts(outerItem->defs->first->data->expressions, flatExps);
  for (expCell = flatExps->first; expCell; expCell = expCell->next)
    {
      if (expCell->data->prodValue == innerList)
	break;
    }
  if ((outerItem->defs->findLength() == 1) &&
      expCell->back && (expIsComma(expCell->back) == false) &&
      expCell->next && (expIsComma(expCell->next) == false));
  else if (alwaysFollowedByNewline(outerList, 4) == true)
    {
      if (innerItem->endsInOptional == true)
	fixConflictListNested1(outerList, outerItem, innerList, innerItem);
      else
	fixConflictListOptional(outerList, outerItem);
    }
  else if (alwaysFollowedByComma(outerList) == true)
    fixConflictListNested2(outerList, outerItem);
}

/********************************************************************/

/* fixConflictListNested1

Returned Value: none

Called By: fixConflictListNested

This is called if all of the following are true:
1. innerList is not used in any list other than outerList.
2. outerList is always followed by a newline.
3. innerList appears in outerItem.
4. At least one definition of innerItem has an optional at the end.

For the changes below to be sufficient to fix the conflict, the innerList
must appear only at the end of one or more definitions of outerItem.
This function is not checking that the innerList does not appear
elsewhere in the definitions of outerItem.

1. Call insertItemInList on the outer list. For example, this changes
   snslctWristList and snslctWristItem, which start as:

   snslctWristList = [snslctWristList, c], snslctWristItem ;

   snslctWristItem = swLabel, c, snslctWristAngleList ;
   
   so that (i) snslctWristItem has its fixType set to fixListItemDeleted,
   (ii) snslctWristList has its fixType set to fixListItemsInserted1,
   and (iii) the definition of snslctWristList is set to the following,
   which uses right recursion with the comma inside the optional at the end.

   snslctWristList =
          swLabel, c, snslctWristAngleList, [c, snslctWristList] ;

2. For each definition of outerList that now has innerList as the
   next-to-last expression, do the following.

 a. Add a definition to outerList that is a copy of the definition in
    which innerList is (now) the next-to-last expression. For example:

    snslctWristList =
          swLabel, c, snslctWristAngleList, [c, snslctWristList]
        | swLabel, c, snslctWristAngleList, [c, snslctWristList] ;

 b. Modify the first definition of outerList using innerList by removing
    the last two items and inserting in lieu thereof
    [innerList], innerItem. For example:

    snslctWristList =
          swLabel, c, [snslctWristAngleList], snslctWristAngleItem
        | swLabel, c, snslctWristAngleList, [c, snslctWristList] ;

 c. Modify the second definition of outerList using innerList by removing
    the last expression and inserting in lieu thereof outerList.
    For example:

    snslctWristList =
          swLabel, c, [snslctWristAngleList], snslctWristAngleItem
        | swLabel, c, snslctWristAngleList, snslctWristList ;

3. Remove the innerItem and the comma in the optional from the
   definition of innerList. For example:

   snslctWristAngleList = [snslctWristAngleList] ;

4. Make enough copies of the definition of innerList that it has
   as many definitions as innerItem. For example:

   snslctWristAngleItem =
	  stringVal, c, angle
	| stringVal, c, featureLabel, [c, FZ, c, angle]
	| stringVal, c, VEC, c, vector, [c, FZ, c, angle] ;

   snslctWristAngleList =
          [snslctWristAngleList]
        | [snslctWristAngleList]
        | [snslctWristAngleList] ;

5. Copy the expressions from the definitions of innerItem onto the ends
   of the corresponding definitions of innerList, and add a comma
   at the end of each. For example:

   snslctWristAngleList =
          [snslctWristAngleList], stringVal, c, angle, c
        | [snslctWristAngleList], stringVal, c, featureLabel,
          [c, FZ, c, angle], c
        | [snslctWristAngleList], stringVal, c, VEC, c, vector,
          [c, FZ, c, angle], c ;

6. Remove outerList from the usedIns of outerItem.

7. Remove innerList from the usedIns of innerItem.

8. Add outerList to the usedIns of innerItem

********************************************

EXAMPLE (the only one in DMIS)

The natural way to define snslctWristList would be:

snslctWristItem =
	  swLabel, c, snslctWristAngleList
	;

snslctWristList =
	  [snslctWristList, c], snslctWristItem
	;

snslctWristAngleItem =
	  stringVal, c, angle
	| stringVal, c, featureLabel, [c, FZ, c, angle]
	| stringVal, c, VEC, c, vector, [c, FZ, c, angle]
	;

snslctWristAngleList =
	  [snslctWristAngleList, c], snslctWristAngleItem
	;

Two of the productions above are changed as follows. snslctWristItem
is no longer used. The definition of snslctWristAngleItem is not changed.

snslctWristList =
	  swLabel, c, [snslctWristAngleList], snslctWristAngleItem
	| swLabel, c, snslctWristAngleList, snslctWristList
	;

snslctWristAngleList =
	  [snslctWristAngleList], stringVal, c, angle, c
	| [snslctWristAngleList], stringVal, c, featureLabel,
	  [c, FZ, c, angle], c
	| [snslctWristAngleList], stringVal, c, VEC, c, vector,
	  [c, FZ, c, angle], c
	;

*/

void fixConflictListNested1( /* ARGUMENTS                              */
 production * outerList,     /* list built using outerItem             */
 production * outerItem,     /* item with a definition using innerList */
 production * innerList,     /* list built using innerItem             */
 production * innerItem)     /* item from which innerList is built     */
{
  expList * exps;
  expression * exp;
  defListCell * defCell;
  optional * opt;

  insertItemInList(outerItem, outerList, true);
  for (defCell = outerList->defs->first; defCell; defCell = defCell->next)
    {
      exp = defCell->data->expressions->last->back->data;
      if (exp->prodValue && (exp->prodValue == innerList))
	{ // next to last expression is innerList
	  outerList->defs->insertAfterCell(defCell, defCell->data->dup());
	  exps = defCell->data->expressions;
	  exps->last = exps->last->back->back; // remove last two
	  delete exps->last->next->next;
	  delete exps->last->next;
	  exps->last->next = 0;
	  opt = new optional(new expList, 1);
	  opt->expressions->pushBack
	    (new expression(NONTERMINAL, innerList->lhs, 0, 0, innerList));
	  exps->pushBack(new expression(OPTIONAL, 0, 0, opt, 0));
	  exps->pushBack
	    (new expression(NONTERMINAL, innerItem->lhs, 0, 0, innerItem));
	  defCell = defCell->next; // now modify copy of previous definition
	  exps = defCell->data->expressions;
	  exps->last = exps->last->back; // remove last one
	  delete exps->last->next;
	  exps->last->next = 0;
	  exps->pushBack
	    (new expression(NONTERMINAL, outerList->lhs, 0, 0, outerList));
	}
    }
  innerItem->usedIn.pushBack(outerList);
  insertItemAndCommaInList(innerItem, innerList);
}

/********************************************************************/

/* fixConflictListNested2

Returned Value:  none

Called By: fixConflictListNested

This is called if all of the following are true:
1. theList is a list that uses a comma in the recursion
2. At least one definition of listItem uses another list.
3. Everywhere theList is used in a definition, it is followed by a comma.

This does the following, which has the effect of switching to right
recursion and inserting a trailing comma in the production for theList.
1. call insertItemInList
2. In every definition that uses theList of every production that uses
   theList, remove the comma after theList.

This is currently assuming theList has not been processed previously.

EXAMPLE

outerList is snsdefBuildItemList
innerList is snsdefWristAngleList

Before conversion:

snsdefBuild =
	  BUILD, c, snsdefBuildItemList, c, snsdefBuildSensor
	;

snsdefBuildSensor =
	  ssLabel
	| sgsLabel
	;

snsdefBuildItemList =
	  [snsdefBuildItemList, c], snsdefBuildItem
	;

snsdefBuildItem =
	  sgLabel
 	| swLabel, [c, snsdefWristAngleList]
 	| sxLabel
 	| rmLabel
	;

snsdefWristAngleList =
	  [snsdefWristAngleList, c], snsdefWristAngleItem
	;

snsdefWristAngleItem =
	  stringVal, c, angle
	;
After the conversion done here:

(the comma after snsdefBuildItemList has been removed)
snsdefBuild =
	  BUILD, c, snsdefBuildItemList, snsdefBuildSensor
	;

(this is not changed)
snsdefBuildSensor =
	  ssLabel
	| sgsLabel
	;

(the definitions of snsdefBuildItem have been moved in, recursion has
changed to right recursion, and the comma is changed to trailing)
snsdefBuildItemList =
	  sgLabel, c, [snsdefBuildItemList]
 	| swLabel, [c, snsdefWristAngleList], c, [snsdefBuildItemList]
  	| sxLabel, c, [snsdefBuildItemList]
 	| rmLabel, c, [snsdefBuildItemList]
	;

(this is not changed)
snsdefWristAngleList =
	  [snsdefWristAngleList, c], snsdefWristAngleItem
	;

(this is not changed)
snsdefWristAngleItem =
	  stringVal, c, angle
	;

snsdefBuildItem is no longer used, so no YACC production for
snsdefBuildItem will be printed.

*/

void fixConflictListNested2( /* ARGUMENTS                            */
 production * theList,       /* the (outer) list to be processed     */
 production * listItem)      /* the item from which theList is built */
{
  defListCell * defCell;     // a definition of user
  prodListCell * prodCell;   // a user production

  insertItemInList(listItem, theList, false);
  for (prodCell = theList->usedIn.first; prodCell; prodCell = prodCell->next)
    { // remove commas after theList in definitions of the users of theList
      for (defCell = prodCell->data->defs->first;
	   defCell;
	   defCell = defCell->next)
	{
	  removeCommaInExps(theList, defCell->data->expressions);
	}
    }
}

/********************************************************************/

/* fixConflictListOptional

Returned Value: none

Called By:
  fixConflictList
  fixConflictListNested

When this is called, listItem is the production defining a list item
for a production defining a list (theList), and it is known that 
either:

1. At least one of the definitions of listItem->newDefs ends in an
   optional starting with a comma, or

2. theList is always followed by a newline and is the outer list for
   an inner list and there are no other complications.

See the documentation of insertItemInList regarding the changes that
are made to theList and listItem.

*/

void fixConflictListOptional( /* ARGUMENTS                    */
 production * theList,        /* a production defining a list */
 production * listItem)       /* the item being listed        */
{
  insertItemInList(listItem, theList, true);
}

/********************************************************************/

/* fixConflictOther

Returned Value: none

Called By: fixConflictsUsers

When this is called, prod is known to be not a list and is known to
have at least one definition that ends in an optional that starts with
a comma. Also, user is known to be a production that uses prod.

In order to determine if there will be a conflict, the definitions of
users are processed to replace optionals and make new definitions
(newUserDefs). The newUserDefs are examined.

If prod is followed by a comma in at least one of the newUserDefs, and
is followed by a comma or a newline wherever it appears in any of the
newUserDefs except at the end of a definition, then:

1. If (i) user is always followed by a newline or (ii) prod never appears
   at the end of any of the newUserDefs, call fixConflictOther1 to
   do the appropriate repair work. Also if prod  (i) is never followed
   by a newline and (ii) never appears at the end of any of the newUserDefs,
   prod will no longer be used by user, so remove is set to 1 to signal
   that fact to the caller.

   For example, tolCompos2, which (i) is followed by a newline in the
   tolCompos production that uses it and (ii) is a user of prod
   tolFeatureMat (which has an optional at the end) will be changed from
   the first production shown below to the second (with the optionals
   expanded).

   tolCompos2 =
	  rentVal, [c, tolMatCond],
          [c, tolFeatureMat, [c, tolFeatureMat, [c, tolFeatureMat]]]

   tolCompos2 =
	  rentVal, [c, tolMatCond],
          [c, [tolFeatureMatC, [tolFeatureMatC]], tolFeatureMat]

   For a second example, tolProfl (another user of tolFeatureMat) has a
   newline at the end of all definitions so that tolFeatureMat never
   appears at the end of the newUserDefs. tolFeatureMat is followed
   by a comma in at least one of the newUserDefs and is always followed
   by either a comma or a newline in all of the newUserDefs. tolProfl
   will be changed from the first production shown below to the second
   (with the optionals expanded).

   tolProfl =
	  tLabel, '=', TOL, '/', PROFL, c, rentVal, c, rentVal,
          [c, tolFeatureMat, [c, tolFeatureMat, [c, tolFeatureMat]]],
	  [c, tolZoneDir2], #

   tolProfl =
	  tLabel, '=', TOL, '/', PROFL, c, rentVal, c, rentVal,
	  [c, [tolFeatureMatC,  [tolFeatureMatC, [tolFeatureMatC]]],
	  tolZoneDir2], #
	| tLabel, '=', TOL, '/', PROFL, c, rentVal, c, rentVal, c,
          [tolFeatureMatC, [tolFeatureMatC]], tolFeatureMat, #

2. Otherwise if:
   A. user is used by only one other production (userUser), and
   B. prod is the last expression in every definition of user, and
   C. user is always followed by a comma in every definition of
      userUser, 
   then call fixConflictOther2 to do the appropriate repair work.
   If fixConflictOther2 was called, prod will have been entirely removed
   from the definitions of user, so remove is set to 1 to signal that
   fact to the caller. 

   For example, tolCompos1, a user of prod tolFeatureMat (which has
   an optional at the end) will be changed from the first production shown
   below to the second (with the optionals expanded). Also, the comma
   following tolCompos1 will be removed from the tolCompos production
   that uses tolCompos1.

   tolCompos1 =
	  rentVal, [c, tolMatCond], c, tolFeatureMat,
          [c, tolFeatureMat, [c, tolFeatureMat]]

   tolCompos1 =
	  rentVal, [c, tolMatCond], c, tolFeatureMatC
          [tolFeatureMatC, [tolFeatureMatC]]

The outer loop here goes through the definitions in the newUserDefs.
The inner loop goes through the expressions of a definition.

beforeComma and beforeNewline are set to false initially. Whenever
prod appears before a comma in the inner loop, beforeComma is set to
true. Whenever prod appears before a newline in the inner loop,
beforeNewline is set to true.  If prod is ever found in the inner loop
not followed by either a comma or a newline, the function returns
without making any changes. 

*ProdC may be required as a replacement for prod in more than one user,
so it may or may not already be defined when this is called. If it is
defined, it is used. If not, it is created.

FIX. This is currently leaking memory via newDefs. Need better destructor
for defList.

*/

void fixConflictOther(    /* ARGUMENTS                                       */
 prodListCell * prodCell, /* cell w productn to check for causing conflicts  */
 production * user,       /* a production using prod                         */
 production ** prodC,     /* production same as prod, but with comma at end  */
 int * remove)            /* set to 1 if user to be remved from prod->usedIn */
{
  production * prod;
  defListCell * defCell;
  defList * newUserDefs;
  defList * newUserUserDefs;
  expListCell * expCell;
  int atEnd;
  bool beforeComma;   // true if prod ever occurs before comma in user defs
  bool beforeNewline; // true if prod ever occurs before newline in user defs
  production * userUser;

  prod = prodCell->data;
  atEnd = 0;
  beforeComma = false;
  beforeNewline = false;
  newUserDefs = user->defs->dup();
  replaceOptsInDefinitions(newUserDefs);
  for (defCell = newUserDefs->first; defCell; defCell = defCell->next)
    {
      expCell = defCell->data->expressions->first;
      if (expCell == 0) // empty definition
	continue;
      for ( ; expCell->next; expCell = expCell->next)
	{
	  if (expIsProd(expCell, prod) == true)
	    {
	      if (expIsComma(expCell->next) == true)
		beforeComma = true;
	      else if (expIsNewline(expCell->next) == true)
		beforeNewline = true;
	      else
		break;
	    }
	}
      if (expCell->next)
	{ // found prod not at end and not followed by comma; do nothing.
	  delete newUserDefs;
	  return;
	}
      if (expIsProd(expCell, prod) == true) 
	atEnd++; // expCell->data is last expression in current definition
    }
  if (beforeComma == false)
    delete newUserDefs; // never found prod before a comma; do nothing.
  else if ((atEnd == 0) || (alwaysFollowedByNewline(user, 4) == true))
    { // either prod never at the end or user is always followed by newline
      delete newUserDefs;
      fixConflictOther1(prodCell, user, prodC);
      if ((atEnd == 0) && (beforeNewline == false))
	*remove = 1;
    }
  else if (user->usedIn.first &&
	   (user->usedIn.first->next == 0) &&
	   (atEnd == newUserDefs->findLength()))
    { // user used in exactly 1 other production & prod ends all defs of user
      delete newUserDefs;
      userUser = user->usedIn.first->data;
      newUserUserDefs = userUser->defs->dup();
      replaceOptsInDefinitions(newUserUserDefs);
      for (defCell = newUserUserDefs->first; defCell; defCell = defCell->next)
	{
	  expCell = defCell->data->expressions->first;
	  if (expCell == 0)
	    continue;
	  for ( ; expCell->next; expCell = expCell->next)
	    {
	      if ((expIsProd(expCell, user) == true) &&
		  (expIsComma(expCell->next) == false))
		break; // found user not followed by comma in userUser
	    }
	  if (expCell->next)
	    break; // found user not followed by comma in userUser
	  if (expIsProd(expCell, user) == true)
	    break; // user occurs at end of userUser, so not followed by comma
	}
      if (defCell == 0)
	{ // user was followed by comma in all definitions of userUser
	  delete userUser->defs;
	  userUser->defs = newUserUserDefs;
	  fixConflictOther2(user->defs, newUserUserDefs,
			    prodCell, user, prodC);
	  *remove = 1;
	}
      else
	{ // user was not followed by comma in all definitions of userUser
	  delete newUserDefs;
	  delete newUserUserDefs;
	}
    }
}

/********************************************************************/

/* fixConflictOther1

Returned Value: none

Called By: fixConflictOther

When this is called, the following facts are known:
1. prod is not a list and has at least one definition that ends in an
   optional that starts with a comma.
2. Either user is always followed by a newline, or prod never appears
   at the end of a definition of user.
3. prod is followed by a comma or a newline wherever it appears in any
   definition of user, except if prod appears at the end of a definition.

This does the following.
1. If *prodC is NULL, *prodC is made.
2. user is added to the users of *prodC.
3. If the fixType of user is fixProdCUser, replaceProdC is called for
   the newDefs of each definition of user. In this case the newDefs of
   each definition of user were generated previously, and a prodC for some
   other prod has already been inserted in at least one of the newDefs.
4. Otherwise, if the fixType of user is fixNone:
   a. newDefs are made for each definition of user
   b. prepareDefsForPlain is called on the newDefs for each definition of user.
   c. replaceProdC is called for each of the newDefs of each definition of
      user. This will change all appearances of "prod, C" in the newDefs
      of the definitions to "prodC".
   d. fixType of user is set to fixProdCUser.
5. Otherwise, an attempt is being made to apply two different kinds of fix,
   so an error message is printed and debnf2pars exits.

See the documentation of replaceProdC for how replacement works.

*/

void fixConflictOther1(   /* ARGUMENTS                                     */
 prodListCell * prodCell, /* cell with used production ending in optional  */
 production * user,       /* production using prod                         */
 production ** prodC)     /* copy of prod w comma added to each definition */
{
  production * prod;
  defListCell * defCell;

  prod = prodCell->data;
  if (*prodC == 0)  
    makeProdC(prodCell, prodC);
  (*prodC)->usedIn.pushBack(user);
  if ((user->fixType == production::fixProdC) ||
      (user->fixType == production::fixProdCUser))
    {
      for(defCell = user->defs->first; defCell; defCell = defCell->next)
	{
	  replaceProdC(defCell->data->newDefs, prod, *prodC, false);
	}
    }
  else if (user->fixType == production::fixNone)
    {
      makeNewDefs(user->defs);
      for(defCell = user->defs->first; defCell; defCell = defCell->next)
	{
	  prepareDefsForPlain(defCell->data->newDefs);
	  replaceProdC(defCell->data->newDefs, prod, *prodC, false);
	}
      user->fixType = production::fixProdCUser;
      printf("%s\n", user->lhs);
    }
  else
    {
      fprintf(stderr, "fix already made in fixConflictOther1\n");
      exit(1);
    }
}

/********************************************************************/

/* fixConflictOther2

Returned Value: none

Called By: fixConflictOther

When this is called, the following facts are known:
1. prod is not a list and has at least one definition that ends in an
   optional that starts with a comma.
2. userDefs are the optional-free def'ns of a prod user (may be many users).
3. prod is the last expression in every definition of user.
4. prod is followed by a comma in at least one definition of user.
5. user is used by only one other production (userUser).
6. user is always followed by a comma in every definition of userUser.
7. userUserDefs are the optional-free definitions of userUser.

This does the following.
1. If *prodC is NULL, *prodC is made.
2. user is added to the users of *prodC.
3. If the fixType of user is fixProdC or fixProdCUser, replaceProdC is
   called for the newDefs of each definition of user. In this case the
   newDefs of each definition of user were generated previously, and a
   prodC for some other prod has already been inserted in at least one
   of the newDefs.
4. Otherwise, if the fixType of user is fixNone:
   a. newDefs are made for each definition of user
   b. prepareDefsForPlain is called on the newDefs for each definition of user.
   c. replaceProdC is called for each of the newDefs of each definition of
      user. This will replace prod with *prodC (i) at the end of every
      definition of user, and (ii) wherever "prod, C" appears in any
      definition of user
   d. fixType of user is set to fixProdCUser.
5. Otherwise, an attempt is being made to apply two different kinds of fix,
   so an error message is printed and debnf2pars exits.
6. The comma following user in the definition(s) of userUser is removed.

This assumes no definition of user and no definition of userUser is empty.

This checks that user is always followed by a comma in every definition of
userUser, and prints an error message and exits if that fails.

*/

void fixConflictOther2(   /* ARGUMENTS                                     */
 defList * userDefs,      /* list of user definitions to change            */
 defList * userUserDefs,  /* list of userUser definitions to change        */
 prodListCell * prodCell, /* cell with used production ending in optional  */
 production * user,       /* production using prod                         */
 production ** prodC)     /* copy of prod w comma added to each definition */
{
  production * prod;
  defListCell * defCell;
  expListCell * expCell;

  prod = prodCell->data;
  if (*prodC == 0)  
    makeProdC(prodCell, prodC);
  if ((user->fixType == production::fixProdC) ||
      (user->fixType == production::fixProdCUser))
    {
      for(defCell = userDefs->first; defCell; defCell = defCell->next)
	{
	  replaceProdC(defCell->data->newDefs, prod, *prodC, true);
	}
    }
  else if (user->fixType == production::fixNone)
    {
      makeNewDefs(userDefs);
      for(defCell = userDefs->first; defCell; defCell = defCell->next)
	{
	  prepareDefsForPlain(defCell->data->newDefs);
	  replaceProdC(defCell->data->newDefs, prod, *prodC, true);
	}
      user->fixType = production::fixProdCUser;
      printf("%s\n", user->lhs);
    }
  else
    {
      fprintf(stderr, "fix already made in fixConflictOther2\n");
      exit(1);
    }
  (*prodC)->usedIn.pushBack(user);
  for (defCell = userUserDefs->first; defCell; defCell = defCell->next)
    { // remove the comma after user in all definitions of userUser
      expCell = defCell->data->expressions->first;
      for ( ; expCell->next; expCell = expCell->next)
	{
	  if (expIsProd(expCell, user) == true)
	    {
	      if (expIsComma(expCell->next) == false)
		{
		  fprintf(stderr, "Bug in fixConflictOther2\n");
		  exit(1);
		}
	      defCell->data->expressions->removeCell(expCell->next);
	    }
	}
    }
}

/********************************************************************/

/* fixConflicts1

Returned Value: none

Called By: main

This function runs before fixConflicts2 since trying to fix two
problems simultaneously is too complex.

This fixes a simple type of construct that will always cause a
shift/reduce conflict. That is a production that [has at least one
definition with an optional at the end preceded by a comma] and
[is always followed by a comma]. When bison sees the comma before
the optional, it will not know if it has seen a whole production
(so it should reduce) or whether it is about to see the optional
part of the production (so it should shift). 

***************************************************

There are two examples of this in DMIS. The first is pameasDetail,
which appears only in pameasVar2ListItem.

pameasDetail =
	  DISTANCE, c, realVal, [c, SCNVEL, c, fedratLinear],
          [c, PITCH, c, realVal]
	| SCNVEL, c, fedratLinear, [c, PITCH, c, realVal]
	| PITCH, c, realVal
	| NODATA
	;

pameasVar2List =
	  [pameasVar2List, c], pameasVar2ListItem
	;

pameasVar2ListItem =
	  pameasDetail, c, pLabel, [c, vector], [c, SELFCENTER, c, vector,
          [c, forceOrDeflection]], [c, pameasRemove], [c, ROTARY, c, rtLabel]
	;

pameasDetail and pameasVar2ListItem will be fixed automatically by
this function as follows.  After the fix shown here, to fix the
problem with the optional at the end of the list item, fixConflicts2
automatically inserts the list item in the list, switches the list to
right recursion, and deletes the list item.

pameasDetail =
	  DISTANCE, c, realVal, [c, SCNVEL, c, fedratLinear],
          [c, PITCH, c, realVal], c
	| SCNVEL, c, fedratLinear, [c, PITCH, c, realVal], c
	| PITCH, c, realVal, c
	| NODATA, c
	;

pameasVar2ListItem =
	  pameasDetail, pLabel, [c, vector], [c, SELFCENTER, c, vector,
          [c, forceOrDeflection]], [c, pameasRemove], [c, ROTARY, c, rtLabel]
	;

***************************************************

The second example is csSpec. It was necessary to change the natural
"[c, csSpec], c" to the slightly odd "c, [csSpec, c]" in two places in
the definition of pathLine in order to get the conversion to be done
automatically.

pathLine =
	  LINE, c, BND, c, CART, c, START, c, point, c, [csSpec, c], END, c,
          point, c, [csSpec, c], VEC, c, vector
	| LINE, c, BND, c, POL, c, point, c, point, c, VEC, c, vector
	;

csSpec =
	  PCS, c, triplet
	| HEADCS, c, rentVal, c, rentVal, [c, rentVal]
	;

These are automatically changed to the following.

pathLine =
	  LINE, c, BND, c, CART, c, START, c, point, c, [csSpec], END, c,
          point, c, [csSpec], VEC, c, vector
	| LINE, c, BND, c, POL, c, point, c, point, c, VEC, c, vector
	;

csSpec =
	  PCS, c, triplet, c
	| HEADCS, c, rentVal, c, rentVal, [c, rentVal], c
	;


***************************************************

This function loops through all the productions, setting each to prod.

If prod is has endsInOptional set to true (which implies prod is not a
list) and prod is always followed by a comma in the expression lists
of the definitions in which it appears, then:

1. The commas following prod are removed from all the expression
lists of all the definitions in which prod appears.

2. A comma is inserted at the end of every definition of prod.

Moving the comma does not require any special action during parsing in
order to parse in terms of the C++ classes built from the unmodified
EBNF, so no fixType or newDefs is recorded.

*/

void fixConflicts1() /* NO ARGUMENTS */
{
  prodListCell * prodCell;
  production * prod;
  prodListCell * userCell;
  defListCell * defCell;

  for (prodCell = productions.first; prodCell; prodCell = prodCell->next)
    {
      prod = prodCell->data;
      if ((prod->endsInOptional == true) &&
	  (alwaysFollowedByComma(prod) == true))
	{
	  for (userCell = prod->usedIn.first;
	       userCell;
	       userCell = userCell->next)
	    { // remove commas after prod in defs of prod users
	      for (defCell = userCell->data->defs->first;
		   defCell;
		   defCell = defCell->next)
		{
		  removeCommaInExps(prod, defCell->data->expressions);
		}
	    }
	  for (defCell = prod->defs->first; defCell; defCell = defCell->next)
	    { // put commas at the ends of defs of prod
	      defCell->data->expressions->pushBack(commaExp);
	    }
	  printf("%s\n", prod->lhs);
	}
    }
}

/********************************************************************/

/* fixConflicts2

Returned Value: none

Called By: main

This loops through all the productions, setting each to prod.

If prod is an unmodified list that uses a comma in the recursion, this
calls fixConflictList to see if there is a conflict and fix it if
so. Otherwise, if prod is definitely not a list and prod has at least
one definition that ends in an optional that starts with a comma, this
calls fixConflictUsers to see if there is a conflict and fix it if so.

A list may be manipulated by this function twice (for example if it is
the outer list in a nested list pair and also has a definition ending
in an optional -- e.g. promptItemList in DMIS). A list should not be
modified twice, so this checks whether each list has been modified and
does not modify it further if so. An unmodified list is known to be as
described in the documentation of production::findIsList.

*/

void fixConflicts2() /* NO ARGUMENTS */
{
  prodListCell * prodCell;
  production * prod;

  for (prodCell = productions.first; prodCell; prodCell = prodCell->next)
    {
      prod = prodCell->data;	
      if ((prod->isList == 2) && (prod->fixType == production::fixNone))
	{
	  fixConflictList(prod);
	}
      else if (prod->endsInOptional == true)
	{
	  fixConflictsUsers(prodCell);
	}
    }
}

/********************************************************************/

/* fixConflictsUsers

Returned Value: none

Called By: fixConflicts2

This is called if prod (the production in prodCell) is not a list and
ends in an optional.  When this happens, it is possible that prod will
cause a shift/reduce conflict when it is used in another
production. So, for each function that uses prod, it is checked
whether a problem occurs, and the problem is fixed if so. Using
productions that are always followed by a newline are handled one way,
using productions that are always followed by a comma are handled
another way.

If a conflict is found and fixed by this function, prodC will be defined
and added to the productions. If not, prodC will always be NULL. ProdC
should only be defined and added once. ProdC is passed around so whether
it already exists can be checked when it is needed.

After fixConflictOther is called, it is possible that prod is no longer
used by the user (userCell->data) being processed. If that occurs, the
user should be removed from the usedIn list of prod, but this happens
while looping down the usedIn list, so the removal needs to be done
carefully to avoid referencing a NULL pointer next time around the loop.

*/

void fixConflictsUsers(   /* ARGUMENTS                                   */
 prodListCell * prodCell) /* cell with production to check for conflicts */
{
  production * prodC; // production the same as prod, but with comma at end
  prodListCell * userCell;
  prodListCell * tempCell;
  int remove; // will be set to 1 if user should be removed from prod->usedIn

  prodC = 0;
  for (userCell = prodCell->data->usedIn.first; userCell; )
    {
      remove = 0;
      fixConflictOther(prodCell, userCell->data, &prodC, &remove);
      if (remove)
	{
	  tempCell = userCell;
	  userCell = userCell->next;
	  prodCell->data->usedIn.removeCell(tempCell);
	}
      else
	userCell = userCell->next;
    }
}

/********************************************************************/

/* flattenOpts

Returned Value: none

Called By:
  fixConflictListNested
  prepareDefForPlain
  printCppClassDerived
  printCppClassTop

This takes an expression list that may have optionals in it and
removes some or all of the optionals by making them non-optional
(as though required).

A. If an optional includes at least one terminal or nonterminal other
than a comma, it is changed to be required.

For example, if the original list of expressions is:

  ifStm, [calibSens], [elseStm, [calibSens]], endifStm

Then the flattened list is:

  ifStm, calibSens, elseStm, calibSens, endifStm

B. If an optional includes no terminals or nonterminals other than
commas (i.e, it consists entirely of optionals, keywords, commas,
oneChars, twoChars, and terminalStrings), then:

B1. If the optional (i) is a simple optional (ii) includes no
optionals and (iii) includes at least one keyword, then the optional
is left as is.

For example, if the original list of expressions is:

   [EXTERN, c, DMIS, c], mLabel

   Then the flattened list is:

   [EXTERN, c, DMIS, c], mLabel

B2. Otherwise, an error message is printed and the program exits.


Since optionals may be nested, this calls itself recursively until it
fails to remove any optionals. When this calls itself recursively,
exps and flatExps are the same, so care has been taken in the construction
of the function both (1) to preserve the sequence of connected list cells
originally contained in exps and (2) to empty flatExps before filling it.

When exps and flatExps are the same, as they are in a recursive call,
setting the first and last of flatExps to NULL near the beginning
disconnects the linked list cells from the flatExps. The for loop
walks down the linked list cells while rebuilding flatExps.

When this is called by printCppClassDerived or printCppClassTop, some
of the flattened expressions will subsequently be assigned an
attName. Since each instance of a nonterminal in the expressions
generated from a multiple optional needs to have a different attName,
the expression list from a multiple optional is duplicated before
putting its expressions into the flattened expression list.

This is currently wasting memory when it is called recursively, since
the cells in the disconnected version of flatExps are not deleted.

*/

void flattenOpts(    /* ARGUMENTS                        */
 expList * exps,     /* expression list to flatten       */
 expList * flatExps) /* flat expression list, built here */
{
  bool foundOptType1; // true=found an opt of optType 1, false=didn't
  int optType;        // 0=can't handle, 1=expand, 2=keyword
  expListCell * expCell;
  expListCell * optExpCell;
  optional * opt;     // optional to process
  int digit;          // digit indicating type of optional

  foundOptType1 = false;
  expCell = exps->first; // do not move this into the "for" loop
  flatExps->first = 0;
  flatExps->last = 0;
  for ( ; expCell; expCell = expCell->next)
    {
      if (expCell->data->theType != OPTIONAL)
	{
	  flatExps->pushBack(expCell->data);
	  continue;
	}
      opt = expCell->data->optValue;
      if (!opt)
	{
	  fprintf(stderr, "Optional missing in flattenOpts\n");
	  exit(1);
	}
      digit = opt->digit;
      if (digit <= 0)
	{
	  fprintf(stderr, "Bad optional count in flattenOpts\n");
	  exit(1);
	}
      optType = findOptType(opt);
      if (optType == 0)
	{
	  fprintf(stderr, "Cannot handle optional in flattenOpts\n");
	  exit(1);
	}
      else if (optType == 1)
	{
	  foundOptType1 = true;
	  for ( ; digit > 0; digit--)
	    {
	      optExpCell = opt->expressions->first;
	      for ( ; optExpCell; optExpCell = optExpCell->next)
		{
		  flatExps->pushBack(optExpCell->data->dup());
		}
	    }
	}
      else // if (optType == 2)
	{ // an optional that contains a keyword
	  flatExps->pushBack(expCell->data);
	}
    }
  if (foundOptType1)
    flattenOpts(flatExps, flatExps);
}

/********************************************************************/

/* insertItemAndCommaInList

Returned Value: none

Called By:  fixConflictListNested1

When this is called:
1. theList->defs contains only one definition; call it listDef.
2. The listDef is in standard left recursive form, i.e. [theList, c], listItem
3. The newDefs of listDef are NULL.

This is checking all three of the above (except for the comma in the optional).

Let prodSet be the set of all productions used in any definition of listItem.

This:
1. creates listDef->newDefs
2. inserts a copy of listDef as the first member of listDef->newDefs;
   call the first member of listDef->newDefs newListDef1.
3. removes the listItem and the comma from newListDef1.
4. puts as many copies of newListDef1 into listDef->newDefs as there are
   definitions of listItem.
5. copies the expressions from each definition in listItem->defs onto
   the end of the corresponding definition in theList->newDefs.
6. sets the className of each definition in theList->newDefs to the
   className of the corresponding definition in listItem->defs;
   this is needed so that the correct class of list item can be
   instantiated in the actions.
7. sets the fixType of theList to fixListItemsInserted1.
8. changes the usedIn list of each production in prodSet by adding theList.
9. removes theList from listItem->usedIn.
10. if listItem->usedIn is then empty (which does not happen in DMIS):
   i. sets fixType of listItem to fixListItemDeleted.
   ii. removes listItem from the usedIns of each production in prodSet.

When this is done, theList still uses left recursion.

*/

void insertItemAndCommaInList( /* ARGUMENTS                                 */
 production * listItem,        /* production from which to copy definitions */
 production * theList)         /* production into which to copy definitions */
{
  defListCell * itemDefCell; // a definition of listItem
  defListCell * listDefCell;
  expList * exps;
  definition * oldDef;       // pointer to first def of theList
  definition * newDef;       // pointer to a new definition
  prodList * prodSet;
  prodListCell * prodCell;

  if (theList->findIsList() == false)
    { // error if theList is not a list in standard format
      fprintf(stderr, "Bad format list in insertItemAndCommaInList\n");
      exit(1);
    }
  oldDef = theList->defs->first->data;
  if (oldDef->newDefs)
    { // error if the first definition of theList already has newDefs
      fprintf(stderr, "List already modified in insertItemAndCommaInList\n");
      exit(1);
    }
  newDef = oldDef->dup();
  newDef->expressions->removeCell(newDef->expressions->last);
  exps = newDef->expressions->first->data->optValue->expressions;
  exps->removeCell(exps->last); // remove comma from optional
  oldDef->newDefs = new defList(newDef);
  itemDefCell = listItem->defs->first;
  for ( ; itemDefCell->next; itemDefCell = itemDefCell->next)
     { // add copies newDef so oldDef->newDefs is same length as listItem defs
      oldDef->newDefs->pushBack(newDef->dup());
    }
  for (itemDefCell = listItem->defs->first,
	 listDefCell = oldDef->newDefs->first;
       itemDefCell;
       itemDefCell = itemDefCell->next, listDefCell = listDefCell->next)
    { // copy defs of listItem into ends of defs of theList and add a comma
      exps = listDefCell->data->expressions;
      exps->spliceInAfterCell(exps->last, itemDefCell->data->expressions);
      exps->pushBack(commaExp);
      listDefCell->data->className = itemDefCell->data->className;
    }
  theList->fixType = production::fixListItemsInserted1;
  printf("%s\n", theList->lhs);
  listItem->usedIn.removeProd(theList);
  prodSet = findProdSet(listItem->defs);
  for (prodCell = prodSet->first; prodCell; prodCell = prodCell->next)
    {
      if (listItem->usedIn.first == 0)
	prodCell->data->usedIn.removeProd(listItem);
      prodCell->data->usedIn.pushBack(theList);
    }
  if (listItem->usedIn.first == 0)
    {
      listItem->fixType = production::fixListItemDeleted;
      printf("%s\n", listItem->lhs);
    }
  delete prodSet;
}

/********************************************************************/

/* insertItemInList

Returned Value: None

Called By:
  fixConflictListNested1
  fixConflictListNested2
  fixConflictListOptional

This function makes new definitions for the (only) definition of a list.
The new definitions are right recursive with the the comma inside the
recursion if commaInside is true and outside (before) the recursion if
commaInside is false, i.e. each new definition follows one of the
following patterns:
  aListItem, [c, theList]
  aListItem, c, [theList]

When this is called:
1. theList->defs contains only one definition; call it listDef.
2. The listDef is in standard left recursive form, i.e. [theList, c], listItem
3. The newDefs of listDef are NULL.

This is checking all three of the above (except for the comma in the optional).

Let prodSet be the set of all productions used in any definition of listItem.

This:
1. creates listDef->newDefs
2. inserts a copy of listDef as the first member of listDef->newDefs;
   call the first member of listDef->newDefs newListDef1.
3. removes the listItem and the comma from newListDef1.
4. inserts a comma in the correct place in newListDef1.
5. puts as many copies of newListDef1 into listDef->newDefs as there are
   definitions of listItem.
6. copies the expressions from each definition in listItem->defs onto
   the beginning of the corresponding definition in theList->newDefs.
7. sets the className of each definition in theList->newDefs to the
   className of the corresponding definition in listItem->defs;
   this is needed so that the correct class of list item can be
   instantiated in the actions.
8. sets the fixType of theList to fixListItemsInserted2.
9. changes the usedIn list of each production in prodSet by adding theList.
10. removes theList from listItem->usedIn.
11. if listItem->usedIn is then empty:
   i. sets fixType of listItem to fixListItemDeleted.
   ii. removes listItem from the usedIns of each production in prodSet.

FIX Maybe remove listItem from productions rather than marking it deleted.

Example with commaInside true

Before:
   displySpecList =
	  [displySpecList, c], displySpecItem
	;

   displySpecItem =
	  device, c, DMIS, [c, vLabel]
	| device, c, vLabel
	;

After
   displySpecList =
	  device, c, DMIS, [c, vLabel], [c, displySpecList]
	| device, c, vLabel, [c, displySpecList]
	;

Example with commaInside false

Before:
   snsdefBuildItemList =
	  [snsdefBuildItemList, c], snsdefBuildItem
	;

   snsdefBuildItem =
	  sgLabel
 	| swLabel, [c, snsdefWristAngleList]
 	| sxLabel
 	| rmLabel
	;

After:
   snsdefBuildItemList =
	  sgLabel, c, [snsdefBuildItemList]
 	| swLabel, [c, snsdefWristAngleList], c, [snsdefBuildItemList]
  	| sxLabel, c, [snsdefBuildItemList]
 	| rmLabel, c, [snsdefBuildItemList]
	;


*/

void insertItemInList(  /* ARGUMENTS                                    */
 production * listItem, /* production from which to copy definitions    */
 production * theList,  /* production into which to copy definitions    */
 bool commaInside)      /* true=comma is inside optional, false=outside */
{
  defListCell * itemDefCell;
  defListCell * listDefCell;
  definition * oldDef;         // pointer to first def of theList
  definition * newDef;         // pointer to a new definition
  expList * exps;
  int n;
  prodList * prodSet;
  prodListCell * prodCell;

  if (theList->findIsList() == false)
    { // error if theList is not a list in standard format
      fprintf(stderr, "Bad format list in insertItemInList\n");
      exit(1);
    }
  oldDef = theList->defs->first->data;
  if (oldDef->newDefs)
    { // error if the first definition of theList already has newDefs
      fprintf(stderr, "List already modified in insertItemInList\n");
      exit(1);
    }
  newDef = oldDef->dup();
  newDef->expressions->removeCell(newDef->expressions->last);
  exps = newDef->expressions->first->data->optValue->expressions;
  exps->removeCell(exps->last); // remove comma from optional
  if (commaInside == true)
    exps->pushFront(commaExp);
  else
    newDef->expressions->pushFront(commaExp);
  oldDef->newDefs = new defList(newDef);
  itemDefCell = listItem->defs->first;
  for ( ; itemDefCell->next; itemDefCell = itemDefCell->next)
    { // add copies newDef so oldDef->newDefs is same length as listItem defs
      oldDef->newDefs->pushBack(newDef->dup());
    }
  n = ((listItem->defs->findLength() == 1) ? 0 : 1);
  for (itemDefCell = listItem->defs->first,
	 listDefCell = oldDef->newDefs->first;
       itemDefCell;
       itemDefCell = itemDefCell->next, listDefCell = listDefCell->next, n++)
    { // copy defs of listItem into front part of defs of theList 
      listDefCell->data->expressions->spliceInFirst
	(itemDefCell->data->expressions);
      listDefCell->data->className = strdup(itemDefCell->data->className);
    }
  theList->fixType = production::fixListItemsInserted2;
  printf("%s\n", theList->lhs);
  listItem->usedIn.removeProd(theList);
  prodSet = findProdSet(listItem->defs);
  for (prodCell = prodSet->first; prodCell; prodCell = prodCell->next)
    {
      if (listItem->usedIn.first == 0)
	prodCell->data->usedIn.removeProd(listItem);
      prodCell->data->usedIn.pushBack(theList);
    }
  if (listItem->usedIn.first == 0)
    {
      listItem->fixType = production::fixListItemDeleted;
      printf("%s\n", listItem->lhs);
    }
  delete prodSet;
}

/********************************************************************/

/* main

This is the main routine for the debnf2yacc parser generator.
This:
1. checks that there is exactly one command argument.
2. opens the EBNF file dmis.debnf for reading and quits if it cannot
   be opened.
3. initializes classDatas, tokenNames, and tokenLexes arrays.
4. parses in the DEBNF file, causing a list of productions to be built
   and populating the three arrays of tokens. For each production a
   list of definitions is built, and it is determined if the production
   represents a list.
5. calls prepareStatementNames to select statements to count uses for.
6. calls reviseSpelling to set up the data that will be used to generate
   correct spellings when the Lex file for the parser being written
   is written.
7. calls productions.findUsedIn to add to each production P a list of
   other productions that use P. The function that does this also examines
   every expression in every definition in every production and, if an
   expression represents a reference to a production; a pointer to the
   production is set in the expression.
8. calls addData to find the class names for every definition of every
   production and to determine which productions will have classes
   that are supertypes or subtypes of classes for other productions.
9. calls selectProductions to select those productions for which
   classes should be printed.
10. calls record Classes to record the names of classes that will be
   printed.
11. calls printCppClasses to print C++ classes to represent the language
   described by the DEBNF file and to provide an application programming
   interface.
12. calls printConfAllSubAtts to print the allSubAtts.cc file.
13. calls printConfAssignMaster to print the assignMasterSubAtts.cc file.
14. calls printConfChecker to print the variable portion of the conformance
   checker.
15. calls printConfTester to print the variable portion of the conformance
   tester.
16. calls fixConflicts1 to modify the list of productions by detecting
   constructs of one simple type that will cause shift/reduce conflicts
   if translated directly into YACC and changing the constructs into
   constructs that allow the same grammar but will not cause shift/reduce
   conflicts if translated directly into YACC. The fix is simply to move
   commas from one production to the ends of the definitions of another
   production.
17. calls fixConflicts2 to modify the list of productions by detecting
   constructs of more complex types that will cause shift/reduce conflicts
   if translated directly into YACC and changing the constructs into
   constructs that allow the same grammar but are more obscure and will
   not cause shift/reduce conflicts if translated directly into YACC.
   The modification process may:
   a. add productions to the list of productions,
   b. mark productions with a code indicating how they were changed
   c. write new definitions for definitions of some productions;
      the new definitions include additional encoded information
      to support printing both rules and actions.
18. calls printYacc to print a YACC file for a parser with the same
   productions as the modified list of productions, using the new
   definitions of each production. Actions that build a parse tree are
   included with each YACC rule. Before printing each definition, if
   needed, new definitions that remove optionals and add encoded
   information are generated.
19. calls printLex to print a Lex file for the parser.

The most difficult task this does is to write the YACC file so that
it builds a parse tree using instances of classes generated from
unmodified productions while executing rules based on modified
productions.

The name of the EBNF file to be processed should end in ".debnf".
When this function is called, there should be one command argument,
which should be the file name without the suffix.

*/

int main(       /* ARGUMENTS                                      */
 int argc,      /* one more than the number of command arguments  */
 char * argv[]) /* array of executable name and command arguments */
{
  int n;
  int m;
  static classData * classDatas[26][CLASSSIZE];
  prodList toPrint;
  char baseName[10];

  sprintf(baseName, "dmis");
  if (argc != 1)
    {
      fprintf(stderr, "Usage: %s\n", argv[0]);
      exit(1);
    }
  yyin = fopen("dmis.debnf", "r");
  if (yyin == 0)
    {
      fprintf(stderr, "unable to open file dmis.debnf for reading\n");
      exit(1);
    }
  for (n = 0; n < 26; n++)
    {
      for (m = 0; m < LETTERSIZE; m++)
	{
	  classDatas[n][m] = 0;
	  tokenNames[n][m] = 0;
	  tokenLexes[n][m] = 0;
	}
    }
  yyparse();
  fclose(yyin);
  prepareStatementNames();
  reviseSpelling();
  productions.findUsedIn();
  addData();
  selectProductions(&toPrint);
  recordClasses(&toPrint, classDatas);
  printCppClasses(baseName, classDatas, &toPrint);
  printConfAllSubAtts(classDatas);
  printConfAssignMaster(classDatas);
  printConfChecker(classDatas);
  printConfTester(classDatas);
  fixConflicts1();
  fixConflicts2();
  printYacc(baseName);
  printLex(baseName);
  return 0;
}

/********************************************************************/

/* makeClassDataAtts

Returned Value: none

Called By:
  printCppClassDerived
  printCppClassTop

This fills in the atts of a classData known to have attributes from the
information in an expression list. Before this function is called, the
list of expressions is expanded to remove optionals (except that type
2 optionals remain).

Attributes are added to the class for terminals, nonterminals,
and optionals in the flatExps. Other expressions in flatExps are
skipped.

The name of each attribute is the attName stored in the corresponding
expression.  See the documentation of findAttributeNames for how
naming is done.

If an expression is a list of nonterminal, then the typeSort of the
attribute made from the expression is set to list, and the typeName of
the attribute is set to the type of the thing listed.

If an expression is a nonterminal that is not a list, then the
typeSort of the attribute is set to ptr, and the typeName of the
attribute is set to the itemName of the expression.

If an expression is a terminal, then it must be an int, double, or char *.
The typeSort of the attribute is set to simple for int and double and to
ptr for char *. The typeName is set to int, double, or char.

If an expression is an optional, then the typeSort is set to simple,
and the typeName is set to bool.

*/

void makeClassDataAtts( /* ARGUMENTS                         */
 expList * flatExps,    /* expressions to represent          */
 classData * data)      /* class data for which to make atts */
{
  expListCell * expCell;
  expression * exp;
  expression * itemExp;
  attCell * aCell;

  aCell = data->atts;
  for (expCell = flatExps->first; expCell; expCell = expCell->next)
    {
      exp = expCell->data;
      if ((exp->theType == NONTERMINAL) ||
	  (exp->theType == TERMINAL) ||
	  (exp->theType == OPTIONAL))
	{
	  if (data->atts == 0)
	    {
	      aCell = new attCell;
	      data->atts = aCell;
	    }
	  else
	    {
	      aCell->next = new attCell;
	      aCell = aCell->next;
	    }
	  aCell->attName = strdup(exp->attName);
	  if (exp->theType == NONTERMINAL)
	    {
	      if (!(exp->prodValue))
		{
		  fprintf(stderr, "Bug in makeClassDataAtts\n");
		  exit(1);
		}
	      if (exp->prodValue->isList)
		{
		  aCell->typeSort = attCell::lst;
		  itemExp =
		    exp->prodValue->defs->first->data->expressions->last->data;
		  aCell->typeName = strdup(itemExp->itemName);
		}
	      else
		{
		  aCell->typeSort = attCell::ptr;
		  aCell->typeName = strdup(exp->itemName);
		}
	    }
	  else if (exp->theType == TERMINAL)
	    {
	      if (strcmp(exp->itemName, "INTSTRING") == 0)
		{
		  aCell->typeSort = attCell::simple;
		  aCell->typeName = strdup("int");
		}
	      else if (strcmp(exp->itemName, "REALSTRING") == 0)
		{
		  aCell->typeSort = attCell::simple;
		  aCell->typeName = strdup("double");
		}
	      else // if (strcmp(exp->itemName, "CHARSTRING") == 0)
		{
		  aCell->typeSort = attCell::ptr;
		  aCell->typeName = strdup("char");
		}
	    }
	  else // if (exp->theType == OPTIONAL)
	    {
	      aCell->typeSort = attCell::simple;
	      aCell->typeName = strdup("bool");
	    }
	}
    }
}

/********************************************************************/

/* makeClassDataSubs

Returned Value: none

Called By: printCppProductionClasses

If a class has subclasses, then the subs of the classData are set.

*/

void makeClassDataSubs(   /* ARGUMENTS                                       */
 production * prod,       /* production for which to populate classData subs */
 classData * classDatas[26][CLASSSIZE]) /* array of class data lists         */
{
  defListCell * defCell; // a definition
  classData * data;      // classData for class
  stringCell * strCell;  // stringCell for subtype name
  
  defCell = prod->defs->first;
  if (defCell == 0)
    { // no definitions
      fprintf(stderr, "Bug in makeClassDataSubs\n");
      exit(1);
    }
  if ((prod->isSupertype == true) || (defCell->next != 0))
    { // find subs for classes with subtypes
      data = findClassData(prod->lhs, classDatas);
      strCell = new stringCell(defCell->data->className);
      data->subs = strCell;
      for (defCell = defCell->next; defCell; defCell = defCell->next)
	{
	  strCell->next = new stringCell(defCell->data->className);
	  strCell = strCell->next;
	}
    }
}

/********************************************************************/

/* makeNewDefs

Returned Value: none

Called By:
  fixConflictOther1
  fixConflictOther2
  makeProdC
  printYaccFirstProduction
  printYaccForPlain

This makes the newDefs for each definition in list of definitions.
If the newDefs already exist for any definition, that is a bug,
and exit is called.

In each case the newDefs includes a single definition, which is a
copy of the original definition.

When this is called by makeProdC, the class names in the defins are
the same as the class names in the production from which the prodC
was made, which is what is desired.

*/

void makeNewDefs(  /* ARGUMENTS                             */
 defList * defins) /* definitions for which to make newDefs */
{
  defListCell * defCell;
  definition * newDef;

  for (defCell = defins->first; defCell; defCell = defCell->next)
    {
      if (defCell->data->newDefs)
	{
	  fprintf(stderr,
		  "definition should not have newDefs in makeNewDefs\n");
	  exit(1);
	}
      newDef = defCell->data->dup();
      defCell->data->newDefs = new defList(newDef);
    }
}

/********************************************************************/

/* makeProdC

Returned Value: None

Called By: 
  fixConflictOther1
  fixConflictOther2

1. *prodC is defined as a new production with the following attributes.
a. lhs (name) is the same as the lhs of prod but with a C added at the end.
b. each definition of prodC is the same as the corresponding definition of
   prod but with a c added at the end.
c. endsInOptional is set to false (since every definition now ends in c).
d. isList is set to the isList of prod.
e. usedIn is set to be empty (by the production constructor).

2. *prodC is added to the usedIn list of every production in every
definition of *prodC.

3. *prodC is added to the list of productions immediately after prodCell.

4. For each definition of *prodC, the newDefs are made and are expanded
   to eliminate optionals and insert nullExp, trueExp, and falseExp
   where needed.

The reason that the newDefs of *prodC are made and expanded here is so
that the newDefs may be modified later if *prodC is a user of another
prodC. This happens in DMIS; tolFeatureXC uses tolMaxMatCondC.

*/

void makeProdC(           /* ARGUMENTS                    */
 prodListCell * prodCell, /* cell with production to copy */
 production ** prodC)     /* production to make           */
{
  production * prod;
  char * newProdName;
  int length;
  defListCell * defCell;
  prodList * prodSet;
  prodListCell * setCell;
  production * newProd;

  prod = prodCell->data;
  length = (strlen(prod->lhs) + 2);
  newProdName = new char[length];
  strcpy(newProdName, prod->lhs);
  newProdName[length - 2] = 'C';
  newProdName[length - 1] = 0;
  newProd = new production;
  newProd->defs = prod->defs->dup();
  newProd->endsInOptional = false;
  newProd->fixType = production::fixProdC;
  newProd->isList = prod->isList;
  newProd->lhs = newProdName;
  printf("%s\n", newProd->lhs);
  for (defCell = newProd->defs->first; defCell; defCell = defCell->next)
    { // add a comma at the end of each definition of *prodC
      defCell->data->expressions->pushBack(commaExp);
    }
  makeNewDefs(newProd->defs);
  for (defCell = newProd->defs->first; defCell; defCell = defCell->next)
    {
      prepareDefsForPlain(defCell->data->newDefs);
    }
  prodSet = findProdSet(newProd->defs);
  for (setCell = prodSet->first; setCell; setCell = setCell->next)
    { // add newProd to usedIn's if missing
      if (setCell->data->usedIn.member(newProd) == 0)
	setCell->data->usedIn.pushBack(newProd);
    }
  productions.insertAfterCell(prodCell, newProd);
  *prodC = newProd;
  delete prodSet;
}

/********************************************************************/

/* prepareDefForPlain

Returned Value: int
  If an optional is removed or simplified, this returns 1.
  Otherwise this returns 0.

Called By: prepareDefsForPlain

For the purposes of this explanation:
1. a type1 optional is an optional whose expressions include
   one or more terminals or nonterminals (it is OK if the expressions
   also include one or more optionals).
2. a type2 optional is an optional that is not a type 1 optional
   but whose expressions contain no optionals and contain at
   least one keyword.

This goes through the expressions of a definition looking for an
optional. If the definition contains an optional this does the
following using the first optional it finds.

1. decreases the digit of the optional by 1.
2. makes a copy of the definition and inserts it in the defs immediately
   after the def.
3. in the original, the optional is removed from the expressions of the
   definition.
4. in the original, this inserts into the expressions of the definition
   (where the optional was) either:
   (1) (if a type1 optional) a nullExp for each terminal and nonterminal
       in the flattened expressions of the optional and a false_exp for
       each type 2 optional in the flattened expressions of the optional, or
   (2) (if a type2 optional) a single falseExp.
5. in the copy, if the digit of the optional was 1, the optional
   is removed from the expressions of the definition.
6. in the copy, if a type2 optional, this inserts a trueExp into the
   expressions of the definition just before where the optional is or was.
7. in the copy, this inserts the expressions of the optional into the
   expressions of the definition just before where the optional is or was.
   (If a trueExp was inserted in in step 6, the insertion in this step
   is immediately after the trueExp.)

In part (1) of step 4, it is necessary to flatten the expressions
of the optional (which makes optionals in those expressions non-optional).

The insertions in steps 4 and 6 are done before the optional so that
existing items from multiple optionals always precede non-existing items.
For example, if the EBNF has b = 3*[d]; where d is a gizmo, then the
C++ code will have a constructor for b with the arguments
 (gizmo * d_1, gizmo * d_2, gizmo * d_3).
Then the actions that get written from the modified EBNF generated
here would be one of:
 $$ = new b(0, 0, 0)
 $$ = new b($1, 0, 0)
 $$ = new b($1, $2, 0)
 $$ = new b($1, $2, $3)
This will be helpful to the applications programmer, who will not have
to test for the other four possible arrangements of $N and NULL.


For example the following three productions would be modified in the
stages shown, by multiple calls to this function. The long arrows on
the left ---> indicate the modified original. The short arrows on the
left --> indicate the modified copy. The arrows on the right <--- point
to the line that will be expanded next.

*****************************
*****************************

bigThing :
	  thingy 2*[C AUTO]  <---

*****************************

bigThing :
--->      thingy falseExp falseExp
-->     | thingy trueExp C AUTO 1*[C AUTO]  <---

*****************************

bigThing :
          thingy falseExp falseExp
--->    | thingy trueExp C AUTO falseExp
-->     | thingy trueExp C AUTO trueExp C AUTO

*****************************

The following is then printed by printYaccForPlain:
bigThing :
          thingy
            { $$ = new bigThing($1, false, false); }
        | thingy C AUTO
            { $$ = new bigThing($1, true, false); }
        | thingy C AUTO C AUTO
            { $$ = new bigThing($1, true, true); }

*****************************
*****************************

tolFlat =
	  FLAT C rentVal [C rentVal C rentVal [C rentVal]]  <---

tolFlat =
--->	  FLAT C rentVal nullExp nullExp  nullExp
-->	| FLAT C rentVal C rentVal C rentVal [C rentVal]  <---

tolFlat =
	  FLAT C rentVal nullExp nullExp nullExp
--->	| FLAT C rentVal C rentVal C rentVal nullExp
-->	| FLAT C rentVal C rentVal C rentVal C rentVal

*****************************

The following is then printed by printYaccForPlain:
tolFlat :
	  FLAT C rentVal
	    { $$ = new tolFlat($3, 0, 0, 0); }
	| FLAT C rentVal C rentVal C rentVal
	    { $$ = new tolFlat($3, $5, $7, 0); }
	| FLAT C rentVal C rentVal C rentVal C rentVal
	    { $$ = new tolFlat($3, $5, $7, $9); }

*****************************
*****************************

datsetSpec =
	  datLabel C orig 2*[C orig]  <---

*****************************

datsetSpec =
--->	  datLabel C orig nullExp nullExp
-->     | datLabel C orig C orig 1*[C orig]  <---

*****************************

datsetSpec =
	  datLabel C orig nullExp nullExp
--->    | datLabel C orig C orig nullExp
-->     | datLabel C orig C orig C orig

*****************************

The following is then printed by printYaccForPlain:
datsetSpec :
	  datLabel C orig
	    { $$ = new datsetSpec($1, $3, 0, 0); }
	| datLabel C orig C orig
	    { $$ = new datsetSpec($1, $3, $5, 0); }
	| datLabel C orig C orig C orig
	    { $$ = new datsetSpec($1, $3, $5, $7); }

*****************************
*****************************

This method of changing the defs makes them suitable for printing both
the YACC rules for the def and the actions that follow the rules. See
the documentation of printYaccForPlain regarding how the encoded data
is used.

This will handle optionals with a digit greater than 1 containing no
terminals or nonterminals (e.g. 2*[AUTO]) in the definition to which
defCell points, but it will not handle them nested inside an optional
in the definition (e.g. [FOO, 2*[AUTO]]). If that happens, findOptType
will return 0; then an error message will be printed and debnf2pars
will exit.

*/

int prepareDefForPlain( /* ARGUMENTS                                 */
 defListCell * defCell, /* definition (from newDefs) to maybe modify */
 defList * defs)        /* the newDefs of a definition, changed here */
{
  expList * exps;            // expressions of original or copied definition
  expListCell * expCell;     // a cell in exps
  expListCell * optCell;     // a cell in exps whose expression is an optional
  expList * flatExps;        // flattened expressions of optional
  expListCell * flatExpCell; // a cell in flatExps
  optional * opt;            // optional that may be found in exps
  int digit;                 // the digit in the optional
  int optType;               // type of optional
  int theType;               // type of expression in flatExps
  int index;                 // 0-based index of expCell in exps
  int n;                     // counter for expressions of copied definition

  exps = defCell->data->expressions;
  index = 0;
  for (expCell = exps->first; expCell; index++, expCell = expCell->next)
    { // set index while iterating so the optional in the copy can be found
      if (expCell->data->theType == OPTIONAL)
	{
	  opt = expCell->data->optValue;
	  if (!opt)
	    {
	      fprintf(stderr, "Optional missing in prepareDefForPlain\n");
	      exit(1);
	    }
	  digit = opt->digit;
	  if (digit <= 0)
	    {
	      fprintf(stderr, "Bad optional count in prepareDefForPlain\n");
	      exit(1);
	    }
	  break;
	}
    }
  if (!expCell)
    return 0; // expressions do not include an optional
  optType = findOptType(opt);
  opt->digit = (digit - 1); // decrease digit by 1 before duplicating
  defs->insertAfterCell(defCell, defCell->data->dup());
  optCell = expCell;
  expCell = expCell->back; //  set insertion location, may be NULL
  exps->removeCell(optCell); // remove optional from original
  // change original first
  if (optType == 1)
    { // optional contains terminal or nonterminal
      flatExps = new expList;
      flattenOpts(opt->expressions, flatExps);
      for (n = 0; n < digit; n++)
	{ // insert falseExp or nullExps in place of opt maximum allowed times
	  flatExpCell = flatExps->first;
	  for ( ; flatExpCell; flatExpCell = flatExpCell->next)
	    { // modify expressions of the original definition
	      theType = flatExpCell->data->theType;
	      if ((theType == TERMINAL) ||
		  (theType == NONTERMINAL) ||
		  (theType == OPTIONAL))
		{ 
		  exps->insertAfterCell
		    (expCell, (theType == OPTIONAL ? falseExp : nullExp));
		  expCell = (expCell ? expCell->next : exps->first);
		}
	    }
	}
    }
  else if (optType == 2)
    { // optional contains keyword but not terminal or nonterminal
      for (n = 0; n < digit; n++)
	{ // insert falseExp in place of opt maximum allowed times
	  exps->insertAfterCell(expCell, falseExp);
	}
    }
  else // if (optType == 0)
    {
      fprintf(stderr, "Cannot handle optional in prepareDefForPlain\n");
      exit(1);
    }
  exps = defCell->next->data->expressions; // now change the copy
  expCell = exps->first;
  for (n = 0; n < index; n++)
    { // find the optional in the copied definition
      expCell = expCell->next;
    }
  optCell = expCell;
  opt = optCell->data->optValue;
  expCell = expCell->back; // set insertion location, may be NULL
  if (digit == 1)
    { // remove optional from copy if completely processed
      exps->removeCell(optCell);
    }
  if (optType == 2)
    { // optional contains keyword but not terminal or nonterminal
      exps->insertAfterCell(expCell, trueExp);
    }
  exps->spliceInAfterCell(expCell, opt->expressions);
  return 1;
}

/********************************************************************/

/* prepareDefsForPlain

Returned Value: none

Called By:
  fixConflictOther1
  fixConflictOther2
  makeProdC
  printYaccFirstProduction
  printYaccForFixDef1
  printYaccForFixDef2
  printYaccForFixList1
  printYaccForPlain

When this starts, the defs are a copy of the original definitions of
a production. This modifies the defs as follows.

For each definition, if the definition contains one or more optionals,
the optionals are expanded in prepareDefForPlain. That function
removes one optional at a time, returning 1 if it makes a change and 0
if it does not. As long as that function returns 1, it is called again.
When it finally returns 0, all the optionals are gone.

*/

void prepareDefsForPlain( /* ARGUMENTS                                  */
 defList * defs)          /* the new defs of a definition, changed here */
{
  defListCell * defCell;

  for (defCell = defs->first; defCell; defCell = defCell->next)
    {
      while (prepareDefForPlain(defCell, defs));
    }
}

/********************************************************************/

/* prepareStatementNames

Returned Value: none

Called By: main

This looks through the productions. If the first definition of a
production ends with ENDLINE and the production is not named
noParseStm, the name of the production is added to the statementNames
list in alphabetical order.

This is assuming that DMIS statements and only DMIS statements have
ENDLINE at the end of the first definition. This is true in the EBNF
files for DMIS.

If there is more than one definition and the first definition ends
with ENDLINE, every definition should end with ENDLINE, but that is
not being checked.

*/

void prepareStatementNames() /* NO ARGUMENTS */
{
  prodListCell * prodCell;
  expListCell * expCell;
  defListCell * defCell;

  for (prodCell = productions.first; prodCell; prodCell = prodCell->next)
    {
      defCell = prodCell->data->defs->first;
      if (defCell == 0)
	continue;
      expCell = defCell->data->expressions->last;
      if (expCell == 0)
	continue;
      if ((expCell->data->theType == ENDLINE) &&
	  strcmp(prodCell->data->lhs, "noParseStm"))
	statementNames.record(prodCell->data->lhs);
    }
}

/********************************************************************/

/* printConfAllSubAtts

Returned Value: none

Called By: printConfChecker

This prints the allSubAtts.cc file from the classDatas array.
The allSubAtts.cc file is hand-edited to produce the 27 files
(cs1lists.cc through unc3lists.cc) that describe the 6 AP levels
and the 21 addendum levels).

If a class has neither attributes nor subtypes (which is true of
all the classes that are really enumeration values), this prints
nothing for that class.

If a class has subtypes (in which case it has no attributes),
this prints something of the following sort. Note that the first
string is the same as the array name. The rest of the strings are
the names of subtypes (of snslctGroup in the example).

const char * snslctGroupSubs[] =
  {"snslctGroupSubtypes",
   "snslctGroupFeat",
   "snslctGroupVec",
   0};

If a class has attributes (in which case it has no subtypes),
this prints something of the following sort. Note that the first
string is the same as the array name. The rest of the strings are
the names of attributes (of snslctGroupFeat in the example).

const char * snslctGroupFeatAtts[] =
  {"snslctGroupFeatAttributes",
   "a_gsaLabel",
   "a_featureLabel",
   "a_angle",
   0};

The classDatas array is kept in alphabetical order by class name.

*/

void printConfAllSubAtts(               /*  ARGUMENTS                */
 classData * classDatas[26][CLASSSIZE]) /* array of class data lists */
{
  FILE * allSubAtts;
  int k;
  int n;
  classData * data;
  stringCell * strCell;
  attCell * aCell;
  char * className;

  allSubAtts = fopen("allSubAtts.cc", "w");
  if (allSubAtts == 0)
    {
      fprintf(stderr, "unable to open file allSubAtts.cc for writing\n");
      exit(1);
    }
  fprintf(allSubAtts, "namespace NDTU {\n");
  for (n = 0; n < 26; n++)
    { // print declarations for all the classes
      for (k=0; classDatas[n][k]; k++)
	{
	  data = classDatas[n][k];
	  className = data->name;
	  if (data->subs && data->atts)
	    {
	      fprintf(stderr, "Bug in printConfAllSubAtts\n");
	      exit(1);
	    }
	  else if (data->subs)
	    {
	      fprintf(allSubAtts, "const char * %sSubs[] =\n", className);
	      fprintf(allSubAtts, "  {\"%sSubs\",\n", className);
	      for (strCell = data->subs; strCell; strCell = strCell->next)
		{
		  fprintf(allSubAtts, "   \"%s\",\n", strCell->data);
		}
	      fprintf(allSubAtts, "   0};\n");
	    }
	  else if (data->atts)
	    {
	      fprintf(allSubAtts, "const char * %sAtts[] =\n", className);
	      fprintf(allSubAtts, "  {\"%sAtts\",\n", className);
	      for (aCell = data->atts; aCell; aCell = aCell->next)
		{
		  fprintf(allSubAtts, "   \"%s\",\n", aCell->attName);
		}
	      fprintf(allSubAtts, "   0};\n");
	    }
	}
    }
  fprintf(allSubAtts, "} // namespace NDTU\n");
  fclose(allSubAtts);
}

/********************************************************************/

/* printConfAnalyzeItems

Returned Value: none

Called By: printConfFunctions

This prints in the checker file the analyzeItems function, preceded by
some documentation.

*/

void printConfAnalyzeItems( /* ARGUMENTS               */
 FILE * checker)            /* parser file to print in */
{
  stringCell * aCell;

  fprintf(checker,
"/* analyzeItems\n"
"\n"
"This is not written using \"else if\"s because some compilers fail when\n"
"there are a lot of them.\n"
"\n"
"*/\n"
"\n"
"void analyzeItems(                   /* ARGUMENTS                         */\n"
" std::list<dmisStatement *> * items) /* list of dmisStatements to examine */\n"
"{\n"
"  std::list<dmisStatement *>::iterator iter;\n"
"\n"
"  for (iter = items->begin(); iter != items->end(); iter++)\n"
"    {\n");
  for (aCell = &statementNames; aCell; aCell = aCell->next)
    {
      fprintf(checker, "      if (isA((*iter), %s))\n", aCell->data);
      fprintf(checker, "\t{ %ss++; continue; }\n", aCell->data);
    }
  fprintf(checker, "    }\n");
  fprintf(checker, "}\n");
}

/********************************************************************/

/* printConfArrays

Returned Value: none

Called By: printConfChecker

This prints an array declaration for each entry in classDatas that has
either subtypes or attributes. For example:

const char * evalStmAtts[3] = {"evalStmAtts", 0};
const char * extensMinorSubs[4] = {"extensMinorSubs", 0};

The size of each array is 2 larger than the number of subtypes or
attributes so that there is room for the first string (which echoes
the array name) and the 0 terminator.

*/

void printConfArrays(                   /*  ARGUMENTS                */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * checker)                        /* file to print to          */
{
  int k;
  int n;
  int size;
  classData * data;
  stringCell * strCell;
  attCell * aCell;
  char * className;

  for (n = 0; n < 26; n++)
    {
      for (k=0; classDatas[n][k]; k++)
	{
	  data = classDatas[n][k];
	  className = data->name;
	  if (data->subs)
	    {
	      for (strCell = data->subs, size = 2;
		   strCell;
		   size++, strCell = strCell->next);
	      fprintf(checker, "const char * %sSubs[%d] = {\"%sSubs\", 0};\n",
		      className, size, className);
	    }
	  else if (data->atts)
	    {
	      for (aCell = data->atts, size = 2;
		   aCell;
		   size++, aCell = aCell->next);
	      fprintf(checker, "const char * %sAtts[%d] = {\"%sAtts\", 0};\n",
		      className, size, className);
	    }
	}
    }
}

/********************************************************************/

/* printConfAssignMaster

Returned Value: none

Called By:  main

This function prints the assignMasterSubAtts.cc file, which contains the
assignMasterSubAtts function. The first few lines of the function this
prints are:

namespace NDTU {
void assignMasterSubAtts()
{
masterSubAtts[0] = aboveBelowSubs;
masterSubAtts[1] = aclratAngSpecSubs;
masterSubAtts[2] = aclratAngularAtts;
masterSubAtts[3] = aclratDefSubs;

The assignMasterSubAtts.cc file is #included by the conformance checker
and the conformance tester, and it is available for other uses.

*/

void printConfAssignMaster(             /* ARGUMENTS                 */
 classData * classDatas[26][CLASSSIZE]) /* array of class data lists */
{
  FILE * outFile;     // file to print to
  int n;              // first index of class Datas
  int k;              // second index of class Datas
  int m;              // index for masterSubAtts
  classData * data;   // classData for class
  char * className;   // name of class

  outFile = fopen("assignMasterSubAtts.cc", "w");
  if (outFile == 0)
    {
      fprintf(stderr, "could not open assignMasterSubAtts.cc for writing\n");
      exit(1);
    }
  fprintf(outFile, "namespace NDTU {\n");
  fprintf(outFile, "void assignMasterSubAtts()\n");
  fprintf(outFile, "{\n");
  m = 0;
  for (n = 0; n < 26; n++)
    {
      for (k=0; classDatas[n][k]; k++)
	{
	  data = classDatas[n][k];
	  className = data->name;
	  if (data->subs && data->atts)
	    {
	      fprintf(stderr, "Bug in printConfAssignMaster\n");
	      exit(1);
	    }
	  else if (data->subs)
	    {
	      fprintf(outFile, "masterSubAtts[%d] = %sSubs;\n", m++, className);
	    }
	  else if (data->atts)
	    {
	      fprintf(outFile, "masterSubAtts[%d] = %sAtts;\n", m++, className);
	    }
	}
    }
  fprintf(outFile, "masterSubAtts[%d] = 0;\n", m);
  fprintf(outFile, "}\n");
  fprintf(outFile, "} // namespace NDTU\n");
  fclose(outFile);
}

/********************************************************************/

/* printConfAttChecker

Returned Value: none

Called By: printConfFunctions

This prints the checking function for a class with attributes.
For each attribute, if the conformance class is not supposed to have
the attribute but the attribute is present, then this prints an error
message.

If the attribute is supposed to be there but is a basic type (double,
int, char *, or bool) or a list of a basic type, then it does not need
checking.

If the attribute is supposed to be there and (1) is a list (but not a
list of a basic type) and (2) is of a type that has either subtypes or
attributes, then this calls the checking function for the type for
each element of the list.

If the attribute is supposed to be there and (1) is not a list and (2)
is not a basic type and (3) has either subtypes or attributes, then this
calls the checking function for the type for the attribute.

Example: check_vformStm is the function that is printed for the
vformStm class. a_vformStm may have two attributes: a_vLabel and
a_vformItemList. vformStmAtts is a list of all attributes allowed in
the conformance class for a vformStm.

void check_vformStm(
 vformStm * a_vformStm)
{
  if (a_vformStm->get_vLabel())
    {
      if (!findString("a_vLabel", vformStmAtts))
	{warnAtt("a_vLabel", "vformStm", a_vformStm);}
      else
        check_vLabel(a_vformStm->get_vLabel());
    }
  if (a_vformStm->get_vformItemList())
    {
      if (!findString("a_vformItemList", vformStmAtts))
	{warnAtt("a_vformItemList", "vformStm", a_vformStm);}
      else
        {
          std::list<vformItem *> * theList;
          std::list<vformItem *>::iterator iter;
          theList = a_vformStm->get_vformItemList();
          for (iter = theList->begin(); iter != theList->end(); iter++)
            check_vformItem(*iter);
        }
    }
}

*/

void printConfAttChecker(               /*  ARGUMENTS                */
 attCell * aCell,                       /* attribute data            */
 char * className,                      /* name of class             */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * checker)                        /* file to print to          */
{
  char * attName;
  char * getName;
  char * typeName;
  bool isList;
  classData * attData;

  fprintf(checker, "void check_%s(\n", className);
  fprintf(checker, " %s * a_%s)\n", className, className);
  fprintf(checker, "{\n");
  for (; aCell; aCell = aCell->next)
    {
      isList = ((aCell->typeSort == attCell::lst) ? true : false);
      attName = aCell->attName;
      typeName = aCell->typeName;
      getName = (((attName[0] == 'a') && (attName[1] == '_')) ?
		 (attName + 2) : attName);
      fprintf(checker, "  if (a_%s->get_%s())\n", className, getName);
      fprintf(checker, "    {\n");
      fprintf(checker, "      if (!findString(\"%s\", %sAtts))\n",
	      attName, className);
      fprintf(checker, "	{warnAtt(\"%s\", \"%s\", a_%s);}\n",
	      attName, className, className);
      if ((strcmp(typeName, "char") == 0) ||
	  (strcmp(typeName, "int") == 0) ||
	  (strcmp(typeName, "double") == 0) ||
	  (strcmp(typeName, "bool") == 0));
      else if (isList)
	{
	  fprintf(checker, "      else\n");
	  fprintf(checker, "        {\n");
	  fprintf(checker, "          std::list<%s *> * theList;\n", typeName);
	  fprintf(checker, "          std::list<%s *>::iterator iter;\n",
		  typeName);
	  fprintf(checker, "          theList = a_%s->get_%s();\n",
		  className, getName);
	  fprintf(checker, "          for (iter = theList->begin(); "
		  "iter != theList->end(); iter++)\n");
	  fprintf(checker, "            check_%s(*iter);\n", typeName);
	  fprintf(checker, "        }\n");
	}
      else
	{
	  attData = findClassData(typeName, classDatas);
	  if ((attData->atts == 0) && (attData->subs == 0))
	    { // attribute class has neither attributes nor subtypes
	      fprintf(checker, "    }\n");
	      continue;
	    }
	  fprintf(checker, "      else\n");
	  fprintf(checker, "        check_%s(a_%s->get_%s());\n",
		  typeName, className, getName);
	}
      fprintf(checker, "    }\n");
    }
  fprintf(checker, "}\n");
  printStarLine(checker);
}

/********************************************************************/

/* printConfBlockAttChecker

Returned Value: none

Called By: printConfFunctions

This prints a function that checks an instance of a class that is
a block and has attributes.

This is very similar to printConfAttChecker except that the error
message printer (warnAttBlock) is different. Also, if an attribute is
a list that is not allowed, then only the first element of the list is
sent to the error message printer.

For example:

void check_doBlock(
 doBlock * a_doBlock)
{
  if (a_doBlock->get_doStm())
    {
      if (!findString("a_doStm", doBlockAtts))
	{warnAttBlock("doStm", a_doBlock->get_doStm());}
      else
        check_doStm(a_doBlock->get_doStm());
    }
  if (a_doBlock->get_dmisItemList())
    {
      if (!findString("a_dmisItemList", doBlockAtts))
	{warnAttBlock("dmisItemList", a_doBlock->get_dmisItemList()->front());}
      else
        {
          std::list<dmisItem *> * theList;
          std::list<dmisItem *>::iterator iter;
          theList = a_doBlock->get_dmisItemList();
          for (iter = theList->begin(); iter != theList->end(); iter++)
            check_dmisItem(*iter);
        }
    }
  if (a_doBlock->get_enddoStm())
    {
      if (!findString("a_enddoStm", doBlockAtts))
	{warnAttBlock("enddoStm", a_doBlock->get_enddoStm());}
    }
}


*/

void printConfBlockAttChecker(          /*  ARGUMENTS                */
 attCell * aCell,                       /* attribute data            */
 char * className,                      /* name of class             */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * checker)                        /* file to print to          */
{
  char * attName;
  char * getName;
  char * typeName;
  bool isList;
  classData * attData;

  fprintf(checker, "void check_%s(\n", className);
  fprintf(checker, " %s * a_%s)\n", className, className);
  fprintf(checker, "{\n");
  for (; aCell; aCell = aCell->next)
    {
      isList = ((aCell->typeSort == attCell::lst) ? true : false);
      attName = aCell->attName;
      typeName = aCell->typeName;
      getName = (((attName[0] == 'a') && (attName[1] == '_')) ?
		 (attName + 2) : attName);
      fprintf(checker, "  if (a_%s->get_%s())\n", className, getName);
      fprintf(checker, "    {\n");
      fprintf(checker, "      if (!findString(\"%s\", %sAtts))\n",
	      attName, className);
      fprintf(checker, "	{warnAttBlock(\"%s\", a_%s->get_%s()%s);}\n",
	      getName, className, getName, (isList ? "->front()" : ""));
      if ((strcmp(typeName, "char") == 0) ||
	  (strcmp(typeName, "int") == 0) ||
	  (strcmp(typeName, "double") == 0) ||
	  (strcmp(typeName, "bool") == 0));
      else if (isList)
	{
	  fprintf(checker, "      else\n");
	  fprintf(checker, "        {\n");
	  fprintf(checker, "          std::list<%s *> * theList;\n", typeName);
	  fprintf(checker, "          std::list<%s *>::iterator iter;\n",
		  typeName);
	  fprintf(checker, "          theList = a_%s->get_%s();\n",
		  className, getName);
	  fprintf(checker, "          for (iter = theList->begin(); "
		  "iter != theList->end(); iter++)\n");
	  fprintf(checker, "            check_%s(*iter);\n", typeName);
	  fprintf(checker, "        }\n");
	}
      else
	{
	  attData = findClassData(typeName, classDatas);
	  if ((attData->atts == 0) && (attData->subs == 0))
	    { // attribute class has neither attributes nor subtypes
	      fprintf(checker, "    }\n");
	      continue;
	    }
	  fprintf(checker, "      else\n");
	  fprintf(checker, "        check_%s(a_%s->get_%s());\n",
		  typeName, className, getName);
	}
      fprintf(checker, "    }\n");
    }
  fprintf(checker, "}\n");
  printStarLine(checker);
}

/********************************************************************/

/* printConfBlockSubChecker

Returned Value: none

Called By: printConfFunctions

This prints a function that checks an instance of a class that is a
block and has subclasses. Every subclass of a class that is a block
should be allowed in every conformance class. For each class name in
the subs list, the function this prints checks whether the instance
belongs to the subclass. When subclass of the instance is found,
the function this writes checks if the subclass is not allowed and
prints a bug message indicating the conformance checker needs fixing
if the subclass is not allowed. If the subclass is allowed, the
function this prints casts the instance into that subclass and
calls the checker for the subclass.

For example (abbreviated):

void check_dmisBlock(
 dmisBlock * a_dmisBlock)
{
  if (isA(a_dmisBlock, calibMasterBlock))
    {
      if (!findString("calibMasterBlock", dmisBlockSubs))
        {warnBlockBug("calibMasterBlock", "dmisBlock");}
      else
        check_calibMasterBlock(dynamic_cast<calibMasterBlock *>(a_dmisBlock));
    }
  else if (isA(a_dmisBlock, calibRtabBlock))
    {
      if (!findString("calibRtabBlock", dmisBlockSubs))
        {warnBlockBug("calibRtabBlock", "dmisBlock");}
      else
        check_calibRtabBlock(dynamic_cast<calibRtabBlock *>(a_dmisBlock));
    }
}

*/

void printConfBlockSubChecker(          /* ARGUMENTS                 */
 stringCell * strCell,                  /* first cell in subs        */
 char * className,                      /* name of class             */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * checker)                        /* file to print to          */
{
  int printElse = 0;
  char * typeName;
  classData * subData;

  fprintf(checker, "void check_%s(\n", className);
  fprintf(checker, " %s * a_%s)\n", className, className);
  fprintf(checker, "{\n");
  for (; strCell; strCell = strCell->next)
    {
      typeName = strCell->data;
      fprintf(checker, "  ");
      if (printElse)
	fprintf(checker, "else ");
      else
	printElse = 1;
      fprintf(checker, "if (isA(a_%s, %s))\n", className, typeName);
      fprintf(checker, "    {\n");
      fprintf(checker, "      if (!findString(\"%s\", %sSubs))\n",
	      typeName, className);
      fprintf(checker, "        {warnBlockBug(\"%s\", \"%s\");}\n",
	      typeName, className);
      subData = findClassData(typeName, classDatas);
      if ((subData->atts) || (subData->subs))
	{
	  fprintf(checker, "      else\n");
	  fprintf(checker, "        check_%s(dynamic_cast<%s *>(a_%s));\n",
		  typeName, typeName, className);
	}
      fprintf(checker, "    }\n");
    }
  fprintf(checker, "}\n");
  printStarLine(checker);  
}

/********************************************************************/

/* printConfChecker

Returned Value: none

Called By: main

This prints the conformance checker.

*/

void printConfChecker(                  /*  ARGUMENTS                */
 classData * classDatas[26][CLASSSIZE]) /* array of class data lists */
{
  FILE * checker;

  checker = fopen("dmisConformanceChecker.cc", "w");
  if (checker == 0)
    {
      fprintf(stderr,
	      "unable to open file dmisConformanceChecker.cc for writing\n");
      exit(1);
    }
  printConfStart(classDatas, checker);
  printStarLine(checker);
  printConfArrays(classDatas, checker);
  printStarLine(checker);
  printConfFunctions(classDatas, checker);
  fprintf(checker,
"} // namespace NDTU\n"
"\n"
"#include \"assignModuleSubAtts.cc\"\n"
"#include \"assignMasterSubAtts.cc\"\n"
"\n"
"namespace NDTU {\n");
  printStarLine(checker);
  printConfReportSummary(checker);
  printStarLine(checker);
  printConfReportSummaryFull(checker);
  printStarLine(checker);
  fprintf(checker, "} // namespace NDTU\n");
  fclose(checker);
}

/********************************************************************/

/* printConfFunctions

Returned Value: none

Called By: printConfChecker

This prints the checker functions. The first function printed is
analyzeItems. The rest of the functions printed are checker functions
for all the classes.

Four different subordinate functions may be called depending on
whether the class:
1. is a DMIS block
2. has attributes
3. has subclasses.

*/

void printConfFunctions(                /*  ARGUMENTS                */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * checker)                        /* file to print to          */
{
  int k;
  int n;
  classData * data;
  char * className;

  printConfAnalyzeItems(checker);
  printStarLine(checker);
  for (n = 0; n < 26; n++)
    {
      for (k=0; classDatas[n][k]; k++)
	{
	  data = classDatas[n][k];
	  className = data->name;
	  if ((data->isBlock) && (data->subs))
	    printConfBlockSubChecker(data->subs, className,classDatas,checker);
	  else if ((data->isBlock) && (data->atts))
	    printConfBlockAttChecker(data->atts, className,classDatas,checker);
	  else if (data->subs)
	    printConfSubChecker(data->subs, className, classDatas, checker);
	  else if (data->atts)
	    printConfAttChecker(data->atts, className, classDatas, checker);
	}
    }
}

/********************************************************************/

/* printConfReportSummary

Returned Value: none

Called By: printConfChecker

This prints in the conformance checker file the reportSummary
function, preceded by some documentation.

In the first two sections of the report it makes, the reportSummary
function marks DMIS statements that are essential for metrology and are
in the conformance class with an M. This function is a bit complex
because it needs to put that functionality into the reportSummary function.

*/

void printConfReportSummary( /* ARGUMENTS                */
 FILE * checker)             /* checker file to print in */
{
  stringCell * aCell;
  int stms;
  int k;                             // index for essentials
  int result;                        //result of strcmp
  static char stmName[64];
  static const char * essentials[] = // commands essential for metrology
    {"const",
     "datdef",
     "datset",
     "endmes",
     "featArc1",
     "featArc2",
     "featCircle",
     "featCompound",
     "featCone",
     "featConradseg",
     "featCparln",
     "featCylndr",
     "featCylradseg",
     "featEdgept",
     "featEllips",
     "featElongcyl",
     "featGcurve",
     "featGeom",
     "featGsurf",
     "featLine",
     "featObject",
     "featParpln",
     "featPatern",
     "featPlane",
     "featPoint",
     "featRctngl",
     "featRevsurf",
     "featSphere",
     "featSphradseg",
     "featSympln",
     "featTorradseg",
     "featTorus",
     "goto",
     "meas",
     "mode",
     "pameas",
     "ptmeas",
     "rmeas",
     "rotate",
     "scnmod",
     "scnset",
     "snsdef",
     "snslct",
     "tolAngl",
     "tolAnglb",
     "tolAnglr",
     "tolAnglwrt",
     "tolCirlty",
     "tolCompos",
     "tolConcen",
     "tolCortol",
     "tolCprofl",
     "tolCprofs",
     "tolCrnout",
     "tolCylcty",
     "tolDiam",
     "tolDistb",
     "tolDistwrt",
     "tolFlat",
     "tolGtol",
     "tolParlel",
     "tolPerp",
     "tolPos",
     "tolProfl",
     "tolProfp",
     "tolProfs",
     "tolRad",
     "tolStrght",
     "tolSym",
     "tolTrnout",
     "tolUsetol",
     "tolWidth",
     "trans",
     "ze end"
    };
  fprintf(checker,
"/* reportSummary\n"
"\n"
"For each statement type T in full DMIS:\n"
"\n"
"A. If T is on theStatements list (i.e., T is included in statement\n"
"   types allowed in the conformance class), then\n"
"1. If T has been used, the number of times it was used is printed.\n"
"2. If T has not been used, it is added to the unusedStms array.\n"
"B. Otherwise (T is not allowed in the conformance class), if T has been\n"
"   used, it is added to the nonoStms array.\n"
"Then this prints the percentage of statements in the conformance class\n"
"that were used, the names of statements that were not used, and the\n"
"names of statements that were used but are not in the conformance class.\n"
"\n"
"Statements in the conformance class that are essential metrology statements\n"
"are marked with an M in the output. They are named in the \"essentials\"\n"
"array.\n"
"\n"
"*/\n"
"\n"
"void reportSummary()\n"
"{\n"
"  int allowed; // counter for the number of statements allowed\n"
"  int used;    // counter for number of allowed statements used\n"
"  int unused;  // counter for number of allowed statements not used\n"
"  int nono;    // counter for number of disallowed statements used\n"
"  int k;       // counter for theStatements (then unusedStms and nonoStms)\n"
"  int j;       // counter for essentials array\n"
"  int result;  // result of strcmp\n"
"  const char * unusedStms[300];\n"
"  const char * nonoStms[300];\n"
"  static const char * essentials[] =\n"
"    {\"const\",\n");
  for (k = 1; k < 73; k++)
    {
      fprintf(checker, "     \"%s\",\n", essentials[k]);
    }
  fprintf(checker,
"     \"ze end\"\n"
"    };\n"
"\n"
"  allowed = 0;\n"
"  used = 0;\n"
"  unused = 0;\n"
"  nono = 0;\n"
"  k = 0;\n"
"\n"
"  printf(\"********************************************\\n\\n\");\n"
"  printf(\"Total statement uses for all files\\n\\n\");\n"
"\n");
  for (stms=0, aCell = &statementNames; aCell; stms++, aCell = aCell->next)
    {
      sprintf(stmName, "%s", aCell->data);
      stmName[strlen(stmName) - 3] = 0; // get rid of Stm suffix
      for (k = 0; ((result = strcmp(essentials[k], stmName)) < 0); k++);
      fprintf(checker, "  if ((theStatements[k]) &&\n");
      fprintf(checker, "      (strcmp(theStatements[k], \"%s\") == 0))\n",
	      aCell->data);
      fprintf(checker, "    {\n");
      fprintf(checker, "      k++;\n");
      fprintf(checker, "      allowed++;\n");
      fprintf(checker, "      if (%ss)\n", aCell->data);
      fprintf(checker, "        {\n");
      fprintf(checker, "          printf(\"%s%s %%d\\n\", %ss);\n",
	      ((result == 0) ? "M " : ""), stmName, aCell->data);
      fprintf(checker, "          used++;\n");
      fprintf(checker, "        }\n");
      fprintf(checker, "      else\n");
      fprintf(checker, "        unusedStms[unused++] = \"%s\";\n", stmName);
      fprintf(checker, "    }\n");
      fprintf(checker, "  else if (%ss)\n", aCell->data);
      fprintf(checker, "    nonoStms[nono++] = \"%s\";\n", stmName);
    }

  fprintf(checker,
"  printf(\n"
"      \"%%.1f%%%% of the commands in the conformance class were used.\\n\",\n"
"	  ((used * 100.0) / allowed));\n"
"  if (unused)\n"
"    {\n"
"      j = 0;\n"
"      printf(\"The following commands were not used:\\n\");\n"
"      for (k = 0; k < unused; k++)\n"
"	{\n"
"	  for (; ((result = strcmp(essentials[j], unusedStms[k])) < 0); j++);\n"
"	  printf(\"%%s%%s\\n\", ((result == 0) ? \"M \" : \"\"), unusedStms[k]);\n"
"	}\n"
"    }\n"
"  if (nono)\n"
"    {\n"
"      printf(\n   "
"    \"The following commands not in the conformance class were used:\\n\");\n"
"      for (k = 0; k < nono; k++)\n"
"        printf(\"%%s\\n\", nonoStms[k]);\n"
"    }\n"
"}\n");
}

/********************************************************************/

/* printConfReportSummaryFull

Returned Value: none

Called By: printConfChecker

This prints in the conformance checker file the reportSummary
function, preceded by some documentation.

*/

void printConfReportSummaryFull( /* ARGUMENTS                */
 FILE * checker)                 /* checker file to print in */
{
  stringCell * aCell;
  int stms;
  static char stmName[64];

  fprintf(checker,
"/* reportSummaryFull\n"
"\n"
"For each statement type T in full DMIS:\n"
"\n"
"1. If T has been used, the number of times it was used is printed.\n"
"2. If T has not been used, it is added to the unusedStms array.\n"
"\n"
"Then this prints the percentage of full DMIS statements that were\n"
"used and the names of statements that were not used.\n"
"\n"
"*/\n"
"\n"
"void reportSummaryFull()\n"
"{\n"
"  int used;    // counter for number of full DMIS statements used\n"
"  int unused;  // counter for number of full DMIS statements not used\n"
"  const char * unusedStms[300];\n"
"  int k;       // index for unused statements\n"
"  used = 0;\n"
"  unused = 0;\n"
"\n"
"  printf(\"********************************************\\n\\n\");\n"
"  printf(\"Total statement uses for all files\\n\\n\");\n"
"\n");
  for (stms=0, aCell = &statementNames; aCell; stms++, aCell = aCell->next)
    {
      sprintf(stmName, "%s", aCell->data);
      stmName[strlen(stmName) - 3] = 0; // get rid of Stm suffix
      fprintf(checker, "  if (%ss)\n", aCell->data);
      fprintf(checker, "    {\n");
      fprintf(checker, "      printf(\"%s %%d\\n\", %ss);\n",
	      stmName, aCell->data);
      fprintf(checker, "      used++;\n");
      fprintf(checker, "    }\n");
      fprintf(checker, "  else\n");
      fprintf(checker, "    unusedStms[unused++] = \"%s\";\n", stmName);
    }

  fprintf(checker,
"  printf(\n"
"      \"%%.1f%%%% of the commands in full DMIS were used.\\n\",\n"
"	  ((used * 100.0) / (used + unused)));\n"
"  if (unused)\n"
"    {\n"
"      printf(\"The following commands were not used:\\n\");\n"
"      for (k = 0; k < unused; k++)\n"
"        printf(\"%%s\\n\", unusedStms[k]);\n"
"    }\n"
"}\n");
}

/********************************************************************/

/* printConfStart

Returned Value: none

Called By: printConfChecker

This prints at the beginning of the checker:
 - disclaimer
 - #include
 - statement counter declarations
 - checker function declarations

Checker function declarations are printed for all classes except those
that have neither subtypes nor attributes.

*/

void printConfStart(                    /*  ARGUMENTS                */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * checker)                        /* file to print to          */
{
  int k;
  int n;
  classData * data;
  stringCell * aCell;
  char * className;

  fprintf(checker,
"/************************************************************************\n"
"  DISCLAIMER:\n"
"  This software was produced by the National Institute of Standards\n"
"  and Technology (NIST), an agency of the U.S. government, and by statute\n"
"  is not subject to copyright in the United States.  Recipients of this\n"
"  software assume all responsibility associated with its operation,\n"
"  modification, maintenance, and subsequent redistribution.\n"
"\n"
"  See NIST Administration Manual 4.09.07 b and Appendix I.\n"
"************************************************************************/\n"
"\n"
"#include \"dmisConformanceCheckerStart.cc\"\n"
"\n"
"namespace NDTU {\n"
"\n");
  for (aCell = &statementNames; aCell; aCell = aCell->next)
    {
      fprintf(checker, "int %ss;\n", aCell->data);
    }
  for (n = 0; n < 26; n++)
    {
      for (k=0; classDatas[n][k]; k++)
	{
	  data = classDatas[n][k];
	  className = data->name;
	  if ((data->subs) || (data->atts))
	    fprintf(checker, "void check_%s(%s * a_%s);\n", className,
		    className, className);
	}
    }
}

/********************************************************************/

/* printConfSubChecker

Returned Value: none

Called By: printConfFunctions

This prints the checking function for a class that has subtypes.  The
function that is printed checks that the subtype of an instance of the
named class is allowed in the conformance class.  If the subtype is
not allowed the checking function prints an error message. If (1) the
subtype is allowed and (2) the subtype is a class that has either
subtypes or attributes, then the checking function for the subtype is
called.

Example: check_windefMinor is the function that is printed for the
windefMinor class. a_windefMinor is either a windefEdgeln or a windefBox.

void check_windefMinor(
 windefMinor * a_windefMinor)
{
  if (isA(a_windefMinor, windefEdgeln))
    {
      if (!findString("windefEdgeln", windefMinorSubs))
        {warnSub("windefEdgeln", "windefMinor", a_windefMinor);}
      else
        check_windefEdgeln(dynamic_cast<windefEdgeln *>(a_windefMinor));
      return;
    }
  if (isA(a_windefMinor, windefBox))
    {
      if (!findString("windefBox", windefMinorSubs))
        {warnSub("windefBox", "windefMinor", a_windefMinor);}
      else
        check_windefBox(dynamic_cast<windefBox *>(a_windefMinor));
      return;
    }
}

When this is called, strCell is the cell whose data is the first
subtype name in a list of subtype names.

This is using multiple "if...return" blocks rather than "if...else if"
blocks because the Visual C++ compiler can only have a limited number of
"if...else if" blocks (each of which causes a deeper nesting), and
dmisFreeStatement has more than the allowed number (which is somewhere
around 100).

*/

void printConfSubChecker(               /*  ARGUMENTS                */
 stringCell * strCell,                  /* cell of first type name   */
 char * className,                      /* name of class             */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * checker)                        /* file to print to          */
{
  char * typeName;
  classData * subData;

  fprintf(checker, "void check_%s(\n", className);
  fprintf(checker, " %s * a_%s)\n", className, className);
  fprintf(checker, "{\n");
  for (; strCell; strCell = strCell->next)
    {
      typeName = strCell->data;
      fprintf(checker, "  if (isA(a_%s, %s))\n", className, typeName);
      fprintf(checker, "    {\n");
      fprintf(checker, "      if (!findString(\"%s\", %sSubs))\n",
	      typeName, className);
      fprintf(checker, "        {warnSub(\"%s\", \"%s\", a_%s);}\n",
	      typeName, className, className);
      subData = findClassData(typeName, classDatas);
      if ((subData->atts) || (subData->subs))
	{
	  fprintf(checker, "      else\n");
	  fprintf(checker, "        check_%s(dynamic_cast<%s *>(a_%s));\n",
		  typeName, typeName, className);
	}
      fprintf(checker, "      return;\n");
      fprintf(checker, "    }\n");
    }
  fprintf(checker, "}\n");
  printStarLine(checker);
}

/********************************************************************/

/* printConfTester

Returned Value: none

Called By: main

This prints the conformance tester.

*/

void printConfTester(                   /*  ARGUMENTS                */
 classData * classDatas[26][CLASSSIZE]) /* array of class data lists */
{
  FILE * tester;

  tester = fopen("dmisConformanceTester.cc", "w");
  if (tester == 0)
    {
      fprintf(stderr,
	      "unable to open file dmisConformanceTester.cc for writing\n");
      exit(1);
    }
  printTestStart(classDatas, tester);
  printStarLine(tester);
  printConfArrays(classDatas, tester);
  printStarLine(tester);
  printTestFunctions(classDatas, tester);
  fprintf(tester, "} // namespace NDTU\n");
  printStarLine(tester);
  fprintf(tester, "#include \"assignModuleSubAtts.cc\"\n");
  printStarLine(tester);
  fprintf(tester, "#include \"assignMasterSubAtts.cc\"\n");
  printStarLine(tester);
  fclose(tester);
}

/********************************************************************/

/* printCppBaseClass

Returned Value: none

Called By: printCppClasses

This prints in the header file and the code file the base class for the
other classes. The base class has:
1. a constructor
2. a virtual destructor
3. a virtual printSelf function.

This also prints the dmisStatementClass, which has only a constructor
and a destructor.

*/

void printCppBaseClass( /* ARGUMENTS                      */
 char * baseClassName,  /* name for base class            */
 FILE * cppHFile,       /* C++ header file to print in    */
 FILE * cppCFile)       /* C++ code file to print in      */
{
  printCppClassStart(baseClassName, cppHFile, cppCFile);
  fprintf(cppHFile,
	  "This is the base class for all other classes. It has a virtual\n"
	  "function printSelf.\n"
	  "\n"
	  "*/\n"
	  "\n");
  fprintf(cppHFile,
	  "class %s\n"
	  "{\n"
	  "public:\n", baseClassName);
  fprintf(cppHFile,
	  "  %s();\n", baseClassName);
  fprintf(cppHFile,	  
	  "  virtual ~%s();\n", baseClassName);
  fprintf(cppHFile,	  
	  "  virtual void printSelf() = 0;\n"
	  "};\n");
	  
  fprintf(cppCFile,
	  "%s::%s(){}\n\n", baseClassName, baseClassName);
  fprintf(cppCFile,	  
	  "%s::~%s(){}\n", baseClassName, baseClassName);
  
  printCppClassStart("dmisStatement", cppHFile, cppCFile);
  fprintf(cppHFile,
	  "This is the dmisStatement class.\n"
	  "\n"
	  "*/\n"
	  "\n"
	  "class dmisStatement :\n"
	  "  public %s\n"
	  "{\n"
	  "public:\n"
	  "  dmisStatement();\n"
	  "  ~dmisStatement();\n"
	  "};\n", baseClassName);
  
  fprintf(cppCFile,
	  "dmisStatement::dmisStatement(){}\n\n"
	  "dmisStatement::~dmisStatement(){}\n\n");
}

/********************************************************************/

/* printCppClassConstructorArgs

Returned Value: none

Called By: printCppClassConstructors

This prints the arguments to a constructor in either the cppHFile
or the cppCFile.

*/

void printCppClassConstructorArgs( /* ARGUMENTS                */
 expList * flatExps,               /* expressions to represent */
 int * printMe,                    /* array of type codes      */
 const char *  spaces,             /* spaces at start of line  */
 FILE * aFile)                     /* C++ header or code file  */
{
  int n;
  expListCell * expCell;
  expression * exp;
  expression * itemExp;
  bool flag;
  char * attName;
  static char aName[NAMESIZE];   // for possibly shortened copy of attName

  flag = false; // now using flag to see if a comma is needed
  for (n = 1, expCell = flatExps->first; expCell; n++, expCell = expCell->next)
    {
      exp = expCell->data;
      if (printMe[n-1])
	{
	  if (flag == true) // printed an argument, so print comma and newline
	    fprintf(aFile, ",\n");
	  else // have not printed any argument yet, so do not print a comma
	    flag = true;
	  attName = exp->attName;
	  strcpy(aName, (((attName[0] == 'a') &&(attName[1] == '_')) ?
			 (attName+2) : attName));
	  if (printMe[n-1] == 1)
	    { // NONTERMINAL
	      if (!(exp->prodValue))
		{
		  fprintf(stderr, "Bug in printCppClassConstructors\n");
		  exit(1);
		}
	      if (exp->prodValue->isList)
		{
		  itemExp =
		    exp->prodValue->defs->first->data->expressions->last->data;
		  fprintf(aFile, "%sstd::list<%s *> * %sIn",
			  spaces, itemExp->itemName, aName);
		}
	      else
		{
		  fprintf(aFile, "%s%s * %sIn",
			  spaces, exp->itemName, aName);
		}
	    }
	  else if (printMe[n-1] == 2)
	    { // INTSTRING TERMINAL
	      fprintf(aFile, "%sint %sIn", spaces, aName);
	    }
	  else if (printMe[n-1] == 3)
	    { // REALSTRING TERMINAL
	      fprintf(aFile, "%sdouble %sIn", spaces, aName);
	    }
	  else if (printMe[n-1] == 4)
	    { // other TERMINAL 
	      fprintf(aFile, "%schar * %sIn", spaces, aName);
	    }
	  else // if (printMe[n-1] == 5)
	    { // optional
	      fprintf(aFile, "%sbool %sIn", spaces, aName);
	    }
	}
    }
}

/********************************************************************/

/* printCppClassConstructors

Returned Value: none

Called By:
  printCppClassDerived
  printCppClassTop

This prints in the header file and code file at least one constructor,
which has no arguments. If the class has data members, this prints a
second constructor, which has an argument for each data member. This
goes through the flatExps at least once in order to figure out if a
second constructor is needed.  If a second constructor is needed, this
calls printCppClassConstructorArgs to print the arguments in the header
file and the code file and then goes through the flatExps again to
print the body of the constructor in the code file.

While determining if a second constructor is needed, the printMe array
of ints is constructed to record what should be done with each
expression in flatExps. The coding is:
0 = do not print
1 = NONTERMINAL
2 = TERMINAL with name INTSTRING
3 = TERMINAL with name STRING
4 = TERMINAL with some other name
5 = optional.

If className is macroBlock, this prints two extra lines for the
macroIsReal attribute.

*/

void printCppClassConstructors( /* ARGUMENTS                   */
 char * className,              /* name of class being printed */
 expList * flatExps,            /* expressions to represent    */
 FILE * cppHFile,               /* C++ header file to print in */
 FILE * cppCFile)               /* C++ code file to print in   */
{
  int n;
  expListCell * expCell;
  expression * exp;
  bool flag;
  int printMe[40];
  char * attName;
  static char aName[NAMESIZE];   // for possibly shortened copy of attName

  fprintf(cppHFile, "  %s();\n", className);
  fprintf(cppCFile, "%s::%s(){}\n", className, className);
  flag = false; // using flag to see if there are any data members
  for (n = 1, expCell = flatExps->first; expCell; n++, expCell = expCell->next)
    {
      exp = expCell->data;
      if (exp->theType == NONTERMINAL)
	{
	  printMe[n-1] = 1;
	  flag = true;
	}
      else if (exp->theType == TERMINAL)
	{
	  printMe[n-1] = ((strcmp(exp->itemName, "INTSTRING") == 0) ? 2 :
			  (strcmp(exp->itemName, "REALSTRING") == 0) ? 3 : 4);
	  flag = true;
	}
      else if (exp->theType == OPTIONAL)
	{
	  printMe[n-1] = 5;
	  flag = true;
	}
      else
	{
	  printMe[n-1] = 0;
	}
    }
  if (flag == false)
    {
      return;
    }
  fprintf(cppHFile, "  %s(\n", className);
  fprintf(cppCFile, "\n");
  fprintf(cppCFile, "%s::%s(\n", className, className);
  printCppClassConstructorArgs(flatExps, printMe, "    ", cppHFile);
  printCppClassConstructorArgs(flatExps, printMe, "  ", cppCFile);
  if (strcmp(className, "macroBlock") == 0)
    {
      fprintf(cppHFile, ",\n");
      fprintf(cppHFile, "    int macroIsRealIn");
      fprintf(cppCFile, ",\n");
      fprintf(cppCFile, "  int macroIsRealIn");
    }
  fprintf(cppHFile, ");\n");
  fprintf(cppCFile, ")\n");
  fprintf(cppCFile, "{\n");
  for (n = 1, expCell = flatExps->first; expCell; n++, expCell = expCell->next)
    {
      exp = expCell->data;
      if ((exp->theType == NONTERMINAL) ||
	  (exp->theType == TERMINAL) ||
	  (exp->theType == OPTIONAL))
	{
	  attName = exp->attName;
	  strcpy(aName, (((attName[0] == 'a') &&(attName[1] == '_')) ?
			 (attName+2) : attName));
	  fprintf(cppCFile, "  %s = %sIn;\n", exp->attName, aName);
	}
    }
  if (strcmp(className, "macroBlock") == 0)
    fprintf(cppCFile, "  macroIsReal = macroIsRealIn;\n");
  fprintf(cppCFile, "}\n");
}

/********************************************************************/

/* printCppClassData

Returned Value: none

Called By:
  printCppClassDerived
  printCppClassTop

This prints in the header file the data members of a class.

*/

void printCppClassData(  /* ARGUMENTS                    */
 classData * data,       /* classData to print from      */
 FILE * cppHFile)        /* C++ header file to print in  */
{
  attCell * aCell;

  aCell = data->atts;
  if (aCell)
    fprintf(cppHFile, "private:\n");
  for ( ; aCell; aCell = aCell->next)
    {
      if (aCell->typeSort == attCell::lst)
	{
	  fprintf(cppHFile, "  std::list<%s *> * %s;\n",
		  aCell->typeName, aCell->attName);
	}
      else if (aCell->typeSort == attCell::ptr)
	{
	  fprintf(cppHFile, "  %s * %s;\n", aCell->typeName, aCell->attName);
	}
      else if (aCell->typeSort == attCell::simple)
	{
	  fprintf(cppHFile, "  %s %s;\n", aCell->typeName, aCell->attName);
	}
      else
	{
	  fprintf(stderr, "bug in printCppClassData\n");
	  exit(1);
	}
    }
}

/********************************************************************/

/* printCppClassDerived

Returned Value: none

Called By: printCppProductionClasses

This prints in the header file and the code file a derived class
representing one of the definitions of a production with two or more
definitions.

The names of the functions this calls indicate what the function does.

*/

void printCppClassDerived( /* ARGUMENTS                           */
 production * parent,      /* parentproduction                    */
 char * parentName,        /* name of parent class,               */
 definition * def,         /* EBNF definition of class            */
 classData * data,         /* classData to record attributes in   */
 FILE * cppHFile,          /* C++ header file to print in         */
 FILE * cppCFile)          /* C++ code file to print in           */
{
  expList * exps;
  expList flatExps;
  char * className;
  
  className = def->className;
  exps = def->expressions;
  flattenOpts(exps, &flatExps);
  findAttributeNames(&flatExps);
  printCppClassStart(className, cppHFile, cppCFile);
  fprintf(cppHFile,
	  "This is a derived class for one of the definitions of %s.\n",
	  parentName);
  fprintf(cppHFile, "It represents the following items:\n\n");
  printCppClassDoc(exps, cppHFile);
  fprintf(cppHFile, "\n\n*/\n\n");
  fprintf(cppHFile, "class %s :\n", className);
  fprintf(cppHFile, "  public %s\n", parentName);
  fprintf(cppHFile, "{\n");
  fprintf(cppHFile, "public:\n");
  printCppClassConstructors(className, &flatExps, cppHFile, cppCFile);
  printCppClassDestructor(className, cppHFile, cppCFile);
  fprintf(cppHFile, "  void printSelf();\n");
  printCppClassPrinter(className, exps, &flatExps, cppCFile);
  makeClassDataAtts(&flatExps, data);
  printCppClassMethods(className, data, cppHFile, cppCFile);
  printCppClassData(data, cppHFile);
  fprintf(cppHFile, "};\n");
}

/********************************************************************/

/* printCppClassDestructor

Returned Value: none

Called By:
  printCppClassDerived
  printCppClassParent
  printCppClassTop

This prints a class destructor in the header file and the code file.
The destructor does nothing.

*/

void printCppClassDestructor( /* ARGUMENTS                   */
 char * className,            /* name for class              */
 FILE * cppHFile,             /* C++ header file to print in */
 FILE * cppCFile)             /* C++ code file to print in   */
{
  fprintf(cppHFile, "  ~%s();\n", className);
  fprintf(cppCFile, "\n");
  fprintf(cppCFile, "%s::~%s(){}\n", className, className);
}

/********************************************************************/

/* printCppClassDoc

Returned Value: none

Called By:
  printCppClassDerived
  printCppClassDoc (recursively for an optional)
  printCppClassTop

This prints in the header file a list of EBNF expressions saying (in
EBNF terms) what DMIS code the class represents. The printing is done
in the documentation of the class being printed.

This documentation is an essential feature because it is the only
source an applications programmer will have to understand what is
in each part of the parse tree.

For example, for vectorFuncVpol, it prints:

VPOL '(' rentVal ',' angle ',' rentVal ')'

*/

void printCppClassDoc( /* ARGUMENTS                    */
 expList * exps,       /* expressions to print         */
 FILE * cppHFile)      /* C++ header file to print in  */
{
  expListCell * expCell;
  expression * exp;

  for (expCell = exps->first; expCell; expCell = expCell->next)
    {
      exp = expCell->data;
      if (exp == commaExp)
	fprintf(cppHFile, "','");
      else if ((exp->theType == NONTERMINAL) || (exp->theType == TERMINAL))
	{
	  fprintf(cppHFile, "%s", exp->itemName);
	}
      else if (exp->theType == KEYWORD)
	{
	  fprintf(cppHFile, "%s", exp->itemName);
	}
      else if (exp->theType == ONECHAR)
	{
	  fprintf(cppHFile, "'%s'", exp->itemName);
	}
      else if (exp->theType == ENDLINE)
	fprintf(cppHFile, "#");
      else if (exp->theType == OPTIONAL)
	{
	  if (exp->optValue->digit > 1)
	    fprintf(cppHFile, "%d*", exp->optValue->digit);
	  fprintf(cppHFile, "%s", ((exp->optValue->digit == 0) ? "{" : "["));
	  printCppClassDoc(exp->optValue->expressions, cppHFile);
	  fprintf(cppHFile, "%s", ((exp->optValue->digit == 0) ? "}" : "]"));
	}
      else if ((exp->theType == TERMINALSTRING) &&
	       (strcmp("**", exp->itemName) == 0))
	fprintf(cppHFile, "'**'");
      else
	{
	  fprintf(stderr, "Bug in printCppClassDoc\n");
	  exit(1);
	}
      if (expCell->next)
	fprintf(cppHFile, " ");
    }
}

/********************************************************************/

/* printCppClasses

Returned Value: none

Called By: main

This prints C++ classes to represent all EBNF productions that are not
for lists, terminals or token names. It prints a C++ header (.hh) file
containing class declarations and a C++ code (.cc) file containing the
implementations of class functions and methods.

A single base class is used at the top of the hierarchy. Its name is
constructed from the name of the EBNF file by removing the ".debnf"
suffix and adding the suffix "CppBase". It has a constructor that does
nothing, a destructor that does nothing, and a virtual printSelf
function. All other classes are derived from the base class.

None of the classes is virtual because it is necessary for applications
to cast non-leaf classes into subtypes, and virtual classes don't
allow that.

Only leaf classes (those that are not the parent of any derived class)
should be instantiated. For the reason just described, the C++ code
does not enforce that rule, which would ordinarily be done by making
non-leaf classes virtual. Only leaf classes have data members and
access functions for the data members (this is a property of the EBNF
for DMIS, not a property of C++).

Parent classes are those that have derived classes.  Parent classes
are needed to make it possible to reference by type something that may
be any of several derived types. Each parent class has only:
 a default constructor,
 a default destructor,
 a printSelf function (implicitly virtual).

The names for classes are constructed by the findClassNames function
and are stored in the className of each definition before this
printCppClasses function is called. The stored names are extracted and
used by the subordinates of this function.

Since C++ compilers will not allow a derived class to be defined in a
header file until all of its base classes have been defined (this
prevents defining loops of class definitions), it is necessary to
ensure that the header file is printed that way. To enable that, the
production class has a boolean wasPrinted data member, originally set
to false, and set to true when class(es) for the production are
printed.

When this function runs, it goes through the productions repeatedly
and prints classes for the productions whose parents have all been
printed (as indicated by wasPrinted=true). While it does that, it
counts the number of productions for which classes have been printed
during each loop and the total number of productions for which classes
have been printed, stopping when either classes have been printed for
all productions or no classes were printed the last time around the
loop. If there is a subtype/supertype loop in the productions, when
printing has stopped, the total number of productions for which
classes have been printed will be less than the number of productions,
indicating an error, and the program exits.

All parent classes are public in their derived classes. All methods
and functions in every class are public. All data members in every
class are private.

This prints the definition of the isA macro in the cppHFile.

This uses the global buffer for file names.

*/

void printCppClasses(                   /* ARGUMENTS                       */
 char * baseFileName,                   /* base name of C++ file to print  */
 classData * classDatas[26][CLASSSIZE], /* class datas to print and add to */
 prodList * toPrint)                    /* list of productions to print    */
{
  FILE * cppHFile;
  FILE * cppCFile;
  production * prod;
  prodListCell * prodCell;
  prodListCell * superCell;
  static char baseClassName[NAMESIZE];
  int n;
  int totalToPrint;  // total number of productions for which to print classes
  int numberPrinted; // number of productions for which classes printed in loop
  int totalPrinted;  // total number of productions for which classes printed

  sprintf(buffer, "%s.hh", baseFileName);
  cppHFile = fopen(buffer, "w");
  if (cppHFile == 0)
    {
      fprintf(stderr, "Unable to open file %s for writing\n", buffer);
      exit(1);
    }
  sprintf(buffer, "%s.cc", baseFileName);
  cppCFile = fopen(buffer, "w");
  if (cppCFile == 0)
    {
      fprintf(stderr, "Unable to open file %s for writing\n", buffer);
      exit(1);
    }
  fprintf(cppCFile, "#include \"%s.hh\"\n", baseFileName);
  fprintf(cppCFile,
"#include <stdio.h>   // for printf, etc.\n"
"\n"
"namespace NDTS {\n"
"\n"
"void printDouble(\n"
" double num)\n"
"{\n"
"  int n;\n"
"  int k;\n"
"  char buffer[50];\n"
"\n"
"  k = sprintf(buffer, \"%%f\", num);\n"
"  for (n = (k-1); ((buffer[n] == '0') && (buffer[n-1] != '.')); n--)\n"
"    buffer[n] = 0;\n"
"  printf(\"%%s\", buffer);\n"
"}\n");
  printCppDocumentation(cppHFile);
  fprintf(cppHFile, 
"#include <list>\n"
"\n"
"namespace NDTS {\n"
"\n"
"#define isA(a,b) dynamic_cast<b *>(a)\n"
"\n");
  sprintf(baseClassName, "%sCppBase", baseFileName);
  printCppNames(baseClassName, classDatas, cppHFile);
  printCppBaseClass(baseClassName, cppHFile, cppCFile);
  totalToPrint = toPrint->findLength();
  totalPrinted = 0;
  for (n = 0; n < totalToPrint; n++)
    {
      numberPrinted = 0;
      for (prodCell = toPrint->first; prodCell; prodCell = prodCell->next)
	{
	  prod = prodCell->data;
	  if (prod->wasPrinted == false)
	    {
	      superCell = prod->subtypeOf.first;
	      for ( ; superCell; superCell = superCell->next)
		{
		  if (superCell->data->wasPrinted == false)
		    break;
		}
	      if (superCell)
		continue; // not all supertypes printed yet
	      printCppProductionClasses(prod, baseClassName, classDatas,
					cppHFile, cppCFile);
	      prod->wasPrinted = true;
	      numberPrinted++;
	    }
	}
      if (numberPrinted == 0)
	break;
      totalPrinted = (totalPrinted + numberPrinted);
    }
  if (totalPrinted != totalToPrint)
    {
      fprintf(stderr, "loop found in productions\n");
      exit(1);
    }
  printStarLine(cppHFile);
  fprintf(cppHFile, "} // namespace NDTS\n");
  fprintf(cppHFile, "#endif\n");
  fclose(cppHFile);
  printStarLine(cppCFile);
  fprintf(cppCFile, "} // namespace NDTS\n");
  fclose(cppCFile);
}

/********************************************************************/

/* printCppClassMethods

Returned Value: none

Called By:
  printCppClassDerived
  printCppClassTop

This prints (1) in the header file the declarations of the get and set
methods for each data member of a class (2) in the code file the
implementations of the get and set methods.  See the documentation of
printCppClassData for more details.

The name of each get method is the prefix "get_" followed by either:
1. the name of the data member if it does not start with "a_", or
2. the name of the data member with the "a_" removed if it starts with "a_".

A get method has no arguments and returns the data member.

The name of each set method is constructed the same way as the
corresponding get method name except the prefix is "set_".

A set method takes one argument, which is an item of the same type as
the data member. The set method sets the data member to the value
given by the argument. A set method does not return anything.

*/

void printCppClassMethods( /* ARGUMENTS                    */
 char * className,         /* name of class               */
 classData * data,         /* classData to print from      */
 FILE * cppHFile,          /* C++ header file to print in  */
 FILE * cppCFile)          /* C++ code file to print in   */
{
  attCell * aCell;
  char * attName;
  char * aName;
  char * typeName;

  aCell = data->atts;
  if (aCell)
    fprintf(cppCFile, "\n");
  for ( ; aCell; aCell = aCell->next)
    {
      attName = aCell->attName;
      aName = (((attName[0] == 'a') && (attName[1] == '_')) ?
	       (attName+2) : attName);
      typeName = aCell->typeName;
      if (aCell->typeSort == attCell::lst)
	{
	  fprintf(cppHFile, "  std::list<%s *> * get_%s();\n",
		  typeName, aName);
	  fprintf(cppCFile, "std::list<%s *> * %s::get_%s()\n",
		  typeName, className, aName);
	  fprintf(cppCFile, "{ return %s; }\n", attName);
	  fprintf(cppHFile, "  void set_%s(std::list<%s *> * %sIn);\n",
		  aName, typeName, aName);
	  fprintf(cppCFile, "void %s::set_%s(std::list<%s *> * %sIn)\n",
		  className, aName, typeName, aName);
	  fprintf(cppCFile, "{ %s = %sIn; }\n", attName, aName);
	}
      else if (aCell->typeSort == attCell::ptr)
	{
	  fprintf(cppHFile, "  %s * get_%s();\n",
		  typeName, aName);
	  fprintf(cppCFile, "%s * %s::get_%s()\n",
		  typeName, className, aName);
	  fprintf(cppCFile, "{ return %s; }\n", attName);
	  fprintf(cppHFile, "  void set_%s(%s * %sIn);\n",
		  aName, typeName, aName);
	  fprintf(cppCFile, "void %s::set_%s(%s * %sIn)\n",
		  className, aName, typeName, aName);
	  fprintf(cppCFile, "{ %s = %sIn; }\n", attName, aName);
	}
      else if (aCell->typeSort == attCell::simple)
	{
	  fprintf(cppHFile, "  %s get_%s();\n",
		  typeName, aName);
	  fprintf(cppCFile, "%s %s::get_%s()\n",
		  typeName, className, aName);
	  fprintf(cppCFile, "{ return %s; }\n", attName);
	  fprintf(cppHFile, "  void set_%s(%s %sIn);\n",
		  aName, typeName, aName);
	  fprintf(cppCFile, "void %s::set_%s(%s %sIn)\n",
		  className, aName, typeName, aName);
	  fprintf(cppCFile, "{ %s = %sIn; }\n", attName, aName);
	}
      else
	{
	  fprintf(stderr, "bug in printCppClassData\n");
	  exit(1);
	}
    }
}

/********************************************************************/

/* printCppClassParent

Returned Value: none

Called By: printCppProductionClasses

This prints in the header file a class with no attributes that will be
used as a parent class. It represents a production that either (1) is
a supertype, or (2) has two or more definitions, at least one of which
is not a single nonterminal.

*/

void printCppClassParent( /* ARGUMENTS                                  */
 char * className,        /* name for class                             */
 prodList * subtypeOf,    /* list of classes of which this is a subtype */
 char * baseClassName,    /* name of base class                         */
 FILE * cppHFile,         /* C++ header file to print in                */
 FILE * cppCFile)         /* C++ code file to print in                  */
{
  prodListCell * prodCell;
  stringCell * strCell;

  prodCell = subtypeOf->first;
  printCppClassStart(className, cppHFile, cppCFile);
  fprintf(cppHFile, "This is a parent class.\n\n*/\n\n");
  fprintf(cppHFile, "class %s :\n", className);
  for (strCell = &statementNames; strCell; strCell = strCell->next)
    {
      if (strcmp(className, strCell->data) == 0)
	break;
    }
  if (prodCell)
    {
      for ( ; prodCell; prodCell = prodCell->next)
	{
	  fprintf(cppHFile, "  public %s%s\n", prodCell->data->lhs,
		  ((prodCell->next || strCell) ? "," : ""));
	}
      if (strCell)
	fprintf(cppHFile, "  public dmisStatement\n");
    }
  else if (strCell)
    fprintf(cppHFile, "  public dmisStatement\n");
  else
    {
      fprintf(cppHFile, "  public %s\n", baseClassName);
    }
  fprintf(cppHFile, "{\n");
  fprintf(cppHFile, "public:\n");
  fprintf(cppHFile, "  %s();\n", className);
  fprintf(cppCFile, "%s::%s(){};\n", className, className);
  printCppClassDestructor(className, cppHFile, cppCFile);
  fprintf(cppHFile, "  void printSelf() = 0;\n");
  fprintf(cppHFile, "};\n");
  
}

/********************************************************************/

/* printCppClassPrinter

Returned Value: none

Called By:
  printCppClassDerived
  printCppClassTop

This prints in the code file. It prints code for a pretty printer for
a class corresponding to a definition of a production. The pretty
printer that is written prints the following items:

1. a blank line
2. the method type and name (on one line); the method takes no arguments.
   The method name is always printSelf.
3. {
4. for macroBlock only, if (macroIsReal){<stuff>} with stuff indented more.
5. one or more lines for each expression in exps, according to the type
   of the expression
   a. comma -  printf(","); (check for comma must be first)
   b. keyword - printf("<keyword name>");
   c. ONECHAR - printf("<the char>");
   d. TERMINALSTRING - printf("<the string>"); (handles '**', POWER operator)
   e. endline - printf("%c%c", 13, 10);
   f. nonterminal that is a list with commas - complex
   g. nonterminal that is a list without commas - complex
   h. nonterminal that is not a list - printf("<attName>->printSelf();\n")
   i. OPTIONAL - complex
   j. terminal INTSTRING - printf("%d", <attName>);\n"
   k. terminal REALSTRING - printf("%f",<attName>);\n" (except versionTag)
   l. terminal char string - printf("%s", <attName>);\n" (except stringConst)
6. }

This works using both the unflattened expressions (exps) and the
flattened expressions (flatExps) for the definition from which the
class is generated. The exps do not have the attribute names but do
have the structure. The flatExps have the attribute names but do not
have the structure.

The exps and flatExps can be matched up by proceeding in step along
both lists, except where optionals occur in the exps. The
printCppClassPrinterOpt function is called to handle an optional in
exps. In that function, an optional is flattened in step with moving
along the flatExps.

To avoid having local variables in the method that this writes (and
generating distinct names for them), the iterator for a list is
declared in a block that goes through the list. This makes it possible
to use the same name, "iter" for all iterator variables, since the
name goes out of scope at the end of the block.

This is writing NULL checks in the pretty printer for those
items that must not be NULL.

This is having the pretty printer print doubles with the default number
of decimal places and no leading zeros, except in the case of
versionTag, which prints version numbers such as 05.1 with two digits
before the decimal point and one after.

This prints WWEOF as EOF. Since EOF means end of file and cannot be
used as a token, it is represented by WWEOF. When it is printed back
out in a DMIS input file, it needs to be printed as EOF.

In order not to print macroBlocks for dummy macros (which are used
so as not to get undefined label errors for CALL/EXTERN,DMIS,M(label)),
macroBlocks have a macroIsReal attribute. The printSelf function this
writes for macroBlock prints nothing if macroIsReal is zero.

EXAMPLE 1

void dmismnStm::printSelf()
{
  printf("DMISMN");
  printf("/");
  a_stringConst->printSelf();
  printf(",");
  a_versionTag->printSelf();
  printf("%c%c", 13, 10);
}

*/

void printCppClassPrinter( /* ARGUMENTS                           */
 char * className,         /* name for class                      */
 expList * exps,           /* expressions to print from           */
 expList * flatExps,       /* flattened exps with attribute names */
 FILE * cppCFile)          /* C++ code file to print in           */
{
  int m; // an index in tokenNames and tokenLexes
  expListCell * expCell; // cell from exps
  expListCell * eCell;   // cell from flatExps
  expression * exp;      // expression in expCell
  expression * e;        // expression in eCell
  expression * itemExp;  // expression for the listed item in a list
  char * lexName;        // name for keyword stored in tokenLexes
  int indent;

  fprintf(cppCFile, "\n");
  fprintf(cppCFile, "void %s::printSelf()\n", className);
  fprintf(cppCFile, "{\n");
  if (strcmp(className, "macroBlock"))
    indent = 2;
  else
    {
      fprintf(cppCFile, "  if (macroIsReal)\n    {\n");
      indent = 6;
    }
  for (expCell = exps->first, eCell = flatExps->first;
       expCell;
       expCell = expCell->next, eCell = eCell->next)
    {
      exp = expCell->data;
      e = eCell->data;
      if (exp == commaExp)
	fprintf(cppCFile, "  printf(\",\");\n");
      else if (exp->theType == KEYWORD)
	{
	  if (!findToken(exp->itemName, &m))
	    {
	      fprintf(stderr, "Bug in printCppClassPrinter\n");
	      exit(1);
	    }
	  lexName = tokenLexes[exp->itemName[0] - 'A'][m];
	  if (strcmp(lexName, "WWEOF") == 0)
	    fprintf(cppCFile, "  printf(\"EOF\");\n");
	  else
	    fprintf(cppCFile, "  printf(\"%s\");\n", lexName);
	}
      else if ((exp->theType == ONECHAR) ||
	       (exp->theType == TERMINALSTRING))
	{
	  fprintf(cppCFile, "  printf(\"%s\");\n", exp->itemName);
	}
      else if (exp->theType == ENDLINE)
	fprintf(cppCFile, "  printf(\"%%c%%c\", 13, 10);\n");
      else if (exp->theType == NONTERMINAL)
	{
	  if (!(exp->prodValue))
	    {
	      fprintf(stderr, "Bug in printCppClassPrinter\n");
	      exit(1);
	    }
	  else if (exp->prodValue->isList == 2)
	    {
	      itemExp =
		exp->prodValue->defs->first->data->expressions->last->data;
	      printCppClassPrinterListYes(itemExp->itemName, e->attName,
					  2, cppCFile);
	    }
	  else if (exp->prodValue->isList == 1)
	    { // list has no commas, "if" not needed, is excuse for block
	      itemExp =
		exp->prodValue->defs->first->data->expressions->last->data;
	      printCppClassPrinterListNo(itemExp->itemName, e->attName,
					 2, 0, cppCFile);
	    }
	  else // if (exp->prodValue->isList == 0)
	    { // not a list, cannot be NULL since parser got it.
	      fprintf(cppCFile, "%s", ((indent == 2) ? "  " : "      "));
	      fprintf(cppCFile, "%s->printSelf();\n", e->attName);
	    }
	}
      else if (exp->theType == TERMINAL)
	{
	  if (strcmp(exp->itemName, "INTSTRING") == 0)
	    fprintf(cppCFile, "  printf(\"%%d\", %s);\n", e->attName);
	  else if (strcmp(exp->itemName, "REALSTRING") == 0)
	    {
	      if (strcmp(className, "versionTag") == 0)
		fprintf(cppCFile, "  printf(\"%%04.1f\", %s);\n", e->attName);
	      else
		//fprintf(cppCFile, "  printf(\"%%f\", %s);\n", e->attName);
		fprintf(cppCFile, "  printDouble(%s);\n", e->attName);
	    }
	  else if (strcmp(className, "stringConst") == 0)
	    {
	      fprintf(cppCFile,
"  int n;\n"
"  putchar('\\'');\n"
"  for (n=0; a_string[n]; n++)\n"
"    {\n"
"      putchar(a_string[n]);\n"
"      if (a_string[n] == '\\'')\n"
"	putchar('\\''); // if apostrophe, print another apostrophe\n"
"    }\n"
"  putchar('\\'');\n");
	    }
	  else // some other class with a char variable
	    fprintf(cppCFile, "  printf(\"%%s\", %s);\n",
		    exp->attName);
	}
      else if (exp->theType == OPTIONAL)
	{
	  if ((!exp->optValue) || (!exp->optValue->expressions))
	    {
	      fprintf(stderr, "bug in printCppClassPrinter\n");
	      exit(1);
	    }
	  printCppClassPrinterOpt(exp->optValue, &eCell, indent, cppCFile);
	}
      if (strcmp(className, "callBlock") == 0)
	break; //print only the call line of a callBlock
    }
  if (indent == 6)
    fprintf(cppCFile, "    }\n");
  fprintf(cppCFile, "}\n");
}

/********************************************************************/

/* printCppClassPrinterListNo

Returned Value: none

Called By:
  printCppClassPrinter
  printCppClassPrinterOpt1

This prints in the code file. It prints the body of the printSelf
method for a DMIS list that is not separated by commas.  The "if (1)"
that this writes first is not needed. It is just a non-ugly way to put
in an extra block to limit the scope of the "iter" local variable. If
the noIf flag is 1, that indicates that the "if (1)" and the braces
enclosing the block can be omitted, because there is an enclosing
block with nothing else in it.

*/

void printCppClassPrinterListNo( /* ARGUMENTS                          */
 char * typeName,                /* name of type of data in the list   */
 char * attName,                 /* attribute name of the list         */
 int spaces,                     /* number of spaces before first line */
 int noIf,                       /* 1=no extra if needed, 0=if needed  */
 FILE * cppCFile)                /* C++ code file to print in          */
{
  int n; // counter for spaces
  
  if (noIf)
    spaces = (spaces - 4);
  else
    {
      for (n=0; n<spaces; n++) fputc(' ', cppCFile);
      fprintf(cppCFile, "if (1)\n");
      for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
      fprintf(cppCFile, "{\n");
    }
  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "std::list<%s *>::iterator iter;\n", typeName);
  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "for (iter = %s->begin();\n", attName);
  for (n=0; n<(spaces+9); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "iter != %s->end();\n", attName);
  for (n=0; n<(spaces+9); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "iter++)\n");
  for (n=0; n<(spaces+6); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "(*iter)->printSelf();\n");
  if (!noIf)
    {
      for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
      fprintf(cppCFile, "}\n");
    }
}

/********************************************************************/

/* printCppClassPrinterListYes

Returned Value: none

Called By:
  printCppClassPrinter
  printCppClassPrinterOpt1

This prints in the code file.  It prints the body of the printSelf
method for a list that has commas separating the list elements. The
"if" that this prints at the beginning is needed in case the list is
empty, since in the "for" loop the address of iter is assumed to point
to something. There are currently no empty lists used in the EBNF for
DMIS (an optional list is used if a list can be empty), but this could
change.

*/


void printCppClassPrinterListYes( /* ARGUMENTS                            */
 char * typeName,                 /* name of type of data in the list     */
 char * attName,                  /* attribute name of the list           */
 int spaces,                      /* number of spaces before first line   */
 FILE * cppCFile)                 /* C++ code file to print in            */
{
  int n; // counter for spaces

  for (n=0; n<spaces; n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "if (%s->begin() == %s->end())\n", attName, attName);
  for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "fprintf(stderr, \"list empty\\n\");\n");
  for (n=0; n<spaces; n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "else\n");
  for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "{\n");
  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "std::list<%s *>::iterator iter;\n", typeName);
  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "for (iter = %s->begin(); ; )\n", attName);
  for (n=0; n<(spaces+6); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "{\n");
  for (n=0; n<(spaces+8); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "(*iter)->printSelf();\n");
  for (n=0; n<(spaces+8); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "if (++iter == %s->end())\n", attName);
  for (n=0; n<(spaces+10); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "break;\n");
  for (n=0; n<(spaces+8); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "printf(\",\");\n");
  for (n=0; n<(spaces+6); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "}\n");
  for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "}\n");
}

/********************************************************************/

/* printCppClassPrinterOpt

Returned Value: none

Called By:
  printCppClassPrinter
  printCppClassPrinterOpt1

This prints in the code file. It prints the body of the printSelf
method for an optional.

To handle a type 1 optional (one whose expressions include at least
one terminal or nonterminal), this calls printCppClassPrinterOpt1.

To handle a type 2 optional (one whose expressions are all constants,
including at least one keyword), this calls printCppClassPrinterOpt2.

If printCppClassPrinterOpt1 is called, before the first call, *eCell
is pointing to the cell of flatExps corresponding to the first
expression of the optional. When printCppClassPrinterOpt1 returns,
*eCell is pointing to the cell of flatExps corresponding to the last
expression of the optional. If another copy of the optional is to be
made, *eCell needs to be set ahead so it again points to the cell of
flatExps corresponding to the first expression of the optional.
That is what is being done on the two lines following the call to
printCppClassPrinterOpt1.

*/

void printCppClassPrinterOpt( /* ARGUMENTS                                 */
 optional * opt,              /* optional to print from                    */
 expListCell ** eCell,        /* 1st flatExp corresponding to opt, changed */
 int spaces,                  /* number spaces at beginning of a line      */
 FILE * cppCFile)             /* C++ code file to print in                 */
{
  int optType;  // type of optional
  int n;

  optType = findOptType(opt);
  if (optType == 2)
    {
      if (opt->digit != 1)
	{
	  fprintf(stderr, "Cannot handle multiple type 2 optional\n");
	  exit(1);
	}
      printCppClassPrinterOpt2(opt->expressions, (*eCell)->data->attName, 
			       spaces, cppCFile);
    }
  else if (optType == 1)
    {
      for (n = 0; n < opt->digit; n++)
	{
	  printCppClassPrinterOpt1(opt->expressions, eCell, spaces, cppCFile);
	  if ((n+1) < opt->digit)
	    *eCell = (*eCell)->next;
	}
    }
  else // if (optType == 0)
    {
      fprintf(stderr, "Bug in printCppClassPrinterOpt\n");
      exit(1);
    }
}

/********************************************************************/

/* printCppClassPrinterOpt1

Returned Value: none

Called By:
  printCppClassPrinterOpt

This prints in the code file. It prints the body of the printSelf
method for an optional of type 1 (one containing at least one terminal
or nonterminal).

This handles nested optionals by calling printCppClassPrinterOpt
recursively.  It does not handle an optional nested inside a multiple
optional (one whose digit is not 1); these do not occur in the EBNF
for DMIS.

To keep track of where it is in the flatExps, this uses the eCell
argument, which is originally pointing to the expression in flatExps
corresponding to the first expression in exps. As this moves through
the exps, it also moves through the flatExps.

To get the spacing right for making the code pretty, this uses the
spaces argument, which indicates the number of spaces to print at the
beginning of the first line.

EXAMPLE

The EBNF for featSphere is the following, which has nested type 1 optionals:

SPHERE ',' matDir ',' typePoint ',' rentVal [',' vector [',' angle]]

The following printSelf function is printed for featSphere:

void featSphere::printSelf()
{
  printf("SPHERE");
  printf(",");
  a_matDir->printSelf();
  printf(",");
  a_typePoint->printSelf();
  printf(",");
  a_rentVal->printSelf();
  if (a_vector)
    {
      printf(",");
      a_vector->printSelf();
      if (a_angle)
        {
          printf(",");
          a_angle->printSelf();
        }
    }
}


*/

void printCppClassPrinterOpt1(/* ARGUMENTS                                  */
 expList * exps,              /* expressions in the optional                */
 expListCell ** eCell,        /* 1st flatExp corresponding to exps, changed */
 int spaces,                  /* number spaces at beginning of a line       */
 FILE * cppCFile)             /* C++ code file to print in                  */
{
  expression * exp;       // the expression in expCell
  expListCell * expCell;  // list cell of exps
  expListCell * eAttCell; // list cell of flatExps starting at *eCell
  int n;                  // space counter
  int m;                  // utility index
  char * lexName;         // printing string for a keyword
  expression * itemExp;

  for (expCell = exps->first, eAttCell = *eCell;
       expCell;
       expCell = expCell->next, eAttCell = eAttCell->next)
    { // find the name of an attribute to test
      exp = expCell->data;
      if ((exp->theType == TERMINAL) || (exp->theType == NONTERMINAL))
	break;
      else if (exp->theType == OPTIONAL)
	{
	  fprintf(stderr, 
		  "Cannot handle nested optional at beginning of optional\n");
	  exit(1);
	}
    }
  if (!expCell)
    {
      fprintf(stderr, "Cannot handle optional in printCppClassPrinterOpt1\n");
      exit(1);
    }
  for (n=0; n<spaces; n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "if (%s)\n", eAttCell->data->attName);
  for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "{\n");
  for (expCell = exps->first;
       expCell;
       expCell = expCell->next)
    {
      exp = expCell->data;
      if (exp == commaExp)
	{
	  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
	  fprintf(cppCFile, "printf(\",\");\n");
	}
      else if (exp->theType == KEYWORD)
	{
	  if (!findToken(exp->itemName, &m))
	    {
	      fprintf(stderr, "Bug in printCppClassPrinter\n");
	      exit(1);
	    }
	  lexName = tokenLexes[exp->itemName[0] - 'A'][m];
	  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
	  if (lexName[0] == '.')
	    fprintf(cppCFile, "printf(\" %s \");\n", lexName);
	  else
	    fprintf(cppCFile, "printf(\"%s\");\n", lexName);
	}
      else if ((exp->theType == ONECHAR) ||
	       (exp->theType == TERMINALSTRING))
	{
	  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
	  fprintf(cppCFile, "printf(\"%s\");\n", exp->itemName);
	}
      else if (exp->theType == NONTERMINAL)
	{
	  if (!(exp->prodValue))
	    {
	      fprintf(stderr, "Bug in printCppClassPrinter\n");
	      exit(1);
	    }
	  else if (exp->prodValue->isList == 2)
	    { // list with commas
	      itemExp = exp->prodValue->defs->first->
		data->expressions->last->data;
	      printCppClassPrinterListYes(itemExp->itemName,
					  (*eCell)->data->attName,
					  (spaces+4), cppCFile);
	    }
	  else if (exp->prodValue->isList == 1)
	    { // list without commas
	      itemExp = exp->prodValue->defs->first->
		data->expressions->last->data;
	      printCppClassPrinterListNo(itemExp->itemName,
					 (*eCell)->data->attName,
					 (spaces+4), (exps->findLength() == 1),
					 cppCFile);
	    }
	  else // if (exp->prodValue->isList == 0)
	    { // not a list, cannot be NULL since parser got it
	      for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
	      fprintf(cppCFile, "%s->printSelf();\n", (*eCell)->data->attName);
	    }
	}
      else if (exp->theType == TERMINAL)
	{
	  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
	  if (strcmp(exp->itemName, "INTSTRING") == 0)
	    fprintf(cppCFile, "printf(\"%%d\", %s);\n",
		    (*eCell)->data->attName);
	  else if (strcmp(exp->itemName, "REALSTRING") == 0)
	    fprintf(cppCFile, "  printDouble(%s);\n", (*eCell)->data->attName);
	  else // CHARSTRING or maybe other char item
	    fprintf(cppCFile, "printf(\"%%s\", %s);\n",
		    (*eCell)->data->attName);
	}
      else if (exp->theType == OPTIONAL)
	{
	  printCppClassPrinterOpt(exp->optValue, eCell, (spaces+4), cppCFile);
	}
      if (expCell->next)
	*eCell = (*eCell)->next;
    }
  for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "}\n");
}

/********************************************************************/

/* printCppClassPrinterOpt2

Returned Value: none

Called By:
  printCppClassPrinterOpt

This prints in the code file. It prints the body of the printSelf
method for an optional of type 2 (one containing only constants,
including at least one keyword).

This does not handle type 2 optionals with digit greater than 1; these
do not occur in the EBNF for DMIS.

To get the spacing right for making the code pretty, this uses the
spaces argument, which indicates the number of spaces to print at the
beginning of the first line.

In the EBNF for DMIS, type 2 optionals include only commas and keywords,
so this function only looks for those two expression types here.

*/

void printCppClassPrinterOpt2(/* ARGUMENTS                            */
 expList * exps,              /* expressions in the optional          */
 char * attName,              /* attribute name                       */
 int spaces,                  /* number spaces at beginning of a line */
 FILE * cppCFile)             /* C++ code file to print in            */
{
  expression * exp;      // the expression in expCell
  expListCell * expCell; // list cell of expressions in the optional
  int n;                 // space counter
  int m;                 // utility index
  char * lexName;        // printing string for a keyword

  for (n=0; n<spaces; n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "if (%s == true)\n", attName);
  for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "{\n");
  for (expCell = exps->first; expCell; expCell = expCell->next)
    {
      exp = expCell->data;
      if (exp == commaExp)
	{
	  for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
	  fprintf(cppCFile, "printf(\",\");\n");
	}
      else if (exp->theType == KEYWORD)
	{
	  if (!findToken(exp->itemName, &m))
	    {
	      fprintf(stderr, "Bug in printCppClassPrinterOpt2\n");
	      exit(1);
	    }
	  lexName = tokenLexes[exp->itemName[0] - 'A'][m];
	  if (lexName[0] == '.')
	    {
	      for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
	      fprintf(cppCFile, "printf(\" %s \");\n", lexName);
	    }
	  else
	    {
	      for (n=0; n<(spaces+4); n++) fputc(' ', cppCFile);
	      fprintf(cppCFile, "printf(\"%s\");\n", lexName);
	    }
	}
    }
  for (n=0; n<(spaces+2); n++) fputc(' ', cppCFile);
  fprintf(cppCFile, "}\n");
}

/********************************************************************/

/* printCppClassStart

Returned Value: none

Called By:
  printCppBaseClass
  printCppClassDerived
  printCppClassParent
  printCppClassTop

This prints in the header file the first five lines of any class
definition. The first, third, and fifth lines are blank. The second
line is a line of stars.  The third line is the class name.

This prints in the code file the first three lines of any class
implementation. The first and third lines are blank. The second
line is a line of stars.

*/

void printCppClassStart( /* ARGUMENTS                    */
 const char * className, /* name for class               */
 FILE * cppHFile,        /* C++ header file to print in  */
 FILE * cppCFile)        /* C++ code file to print in    */
{
  printStarLine(cppCFile);
  printStarLine(cppHFile);
  fprintf(cppHFile,
"/* %s\n"
"\n",
	  className);
}

/********************************************************************/

/* printCppClassTop

Returned Value: none

Called By: printCppProductionClasses

When the production being represented has only one definition, this
(1) prints in the header file the declarations for a class and
(2) prints in the code file the implementation of the class's methods
and functions. The class is a subclass of the base class.

If className is macroBlock, this also prints the macroIsReal data member.

*/

void printCppClassTop( /* ARGUMENTS                                  */
 char * className,     /* name for class                             */
 prodList * subtypeOf, /* list of classes of which this is a subtype */
 char * baseClassName, /* name of base class                         */
 definition * def,     /* EBNF definition of class                   */
 classData * data,     /* classData to fill in                       */
 FILE * cppHFile,      /* C++ header file to print in                */
 FILE * cppCFile)      /* C++ code file to print in                  */
{
  expList * exps;
  expList flatExps;
  prodListCell * prodCell;
  stringCell * strCell;    // iterator for statementNames

  prodCell = subtypeOf->first;
  exps = def->expressions;
  flattenOpts(exps, &flatExps);
  findAttributeNames(&flatExps);
  printCppClassStart(className, cppHFile, cppCFile);
  fprintf(cppHFile,
	  "This is a class for the single definition of %s.\n", className);
  fprintf(cppHFile, "It represents the following items:\n\n");
  printCppClassDoc(exps, cppHFile);
  fprintf(cppHFile, "\n\n*/\n\n");
  fprintf(cppHFile, "class %s :\n", className);
  for (strCell = &statementNames; strCell; strCell = strCell->next)
    {
      if (strcmp(className, strCell->data) == 0)
	break;
    }
  if (prodCell)
    {
      for ( ; prodCell; prodCell = prodCell->next)
	{
	  fprintf(cppHFile, "  public %s%s\n", prodCell->data->lhs,
		  ((prodCell->next || strCell) ? "," : ""));
	}
      if (strCell)
	fprintf(cppHFile, "  public dmisStatement\n");
    }
  else if (strCell)
    fprintf(cppHFile, "  public dmisStatement\n");
  else
    {
      fprintf(cppHFile, "  public %s\n", baseClassName);
    }
  fprintf(cppHFile, "{\n");
  fprintf(cppHFile, "public:\n");
  printCppClassConstructors(className, &flatExps, cppHFile, cppCFile);
  printCppClassDestructor(className, cppHFile, cppCFile);
  fprintf(cppHFile, "  void printSelf();\n");
  printCppClassPrinter(className, exps, &flatExps, cppCFile);
  makeClassDataAtts(&flatExps, data);
  printCppClassMethods(className, data, cppHFile, cppCFile);
  printCppClassData(data, cppHFile);
  if (strcmp(className, "macroBlock") == 0)
    fprintf(cppHFile, "  int macroIsReal;\n");
  fprintf(cppHFile, "};\n");
}

/********************************************************************/

/* printCppDocumentation

Returned Value: none

Called By: printCppClasses

This prints the documentation at the beginning of the C++ header file.

*/

void printCppDocumentation(
 FILE * cppHFile)
{
  fprintf(cppHFile, 
"#ifndef DMIS_HH\n"
"#define DMIS_HH\n"
"/*\n"
"\n"
"This file defines classes used to represent data in program files.\n"
"The meaning of each class definition is documented. In the\n"
"documentation, punctuation marks and special characters that should\n"
"appear in program files are enclosed in quotes. Special characters\n"
"such as square brackets [] used to represent items that are optional\n"
"in program files are used without quotes.\n"
"\n"
"A class is defined for each definition of each production in the EBNF\n"
"file that does not define a list or give the spelling of a token. In\n"
"addition, a parent class is defined for each production that has two\n"
"or more definitions.\n"
"\n"
"Items such as command names and commas that are always the same in\n"
"every instance of a class are not represented in the class. Only those\n"
"items than can differ between instances are represented. For example\n"
"a boundStm has the EBNF form \"BOUND '/' boundMinor #\". The only\n"
"thing in a boundStm that varies between instances is the boundMinor, so\n"
"only the boundMinor is represented in the boundStm class.\n"
"\n"
"Each item from an EBNF definition that is represented in a C++ class for\n"
"the definition is represented by a data member of the class. Each\n"
"represented item is either a list or a class, and they are represented\n"
"as follows.\n"
"\n"
"   Each list data member is represented by a pointer to a C++ standard\n"
"   library list of pointers to the class for the item of which the list\n"
"   is composed. For example, a dmisItemList is a list of dmisItem, and\n"
"   inputFile uses dmisItemList, so in the definition of the C++ inputFile\n"
"   class, there is the line \"std::list<dmisItem *> * a_dmisItemList;\".\n"
"\n"
"   Every other data member is represented by a pointer to the class for\n"
"   that item. For example the boundStm class has only one data member.\n"
"   The data member is a pointer to a boundMinor. In the definition of\n"
"   the boundStm class, there is the line \"boundMinor * a_boundMinor;\"\n"
"\n"
"Optional items for terminals and nonterminals are represented the same\n"
"way as non-optional items. The only difference is that when an\n"
"instance of a class is built, any pointer to an optional item may be\n"
"NULL, while no pointer to a non-optional item may be NULL.\n"
"\n"
"The entire set of optional items in an optional containing only\n"
"constants (no terminal or nonterminal) are represented by a single\n"
"boolean. The boolean is true if the optional is present and false if\n"
"not.\n"
"\n"
"For each data member of a C++ class, there is a method to get the data\n"
"member, and a method to set it. The name of each method is the name of the\n"
"data member preceded by either \"get_\" or \"set_\". The get method\n"
"has no arguments and returns the type of data in the data member. The set\n"
"method has an argument whose type is the type of the data member and\n"
"returns nothing.\n"
"\n"
"Each class from which no other class is derived has two constructors and\n"
"a destructor. One of the constructors takes no arguments, and all it does\n"
"is set the \"typ\" data member of the base class. The other constructor\n"
"takes as many arguments as there are data members and sets all the data\n"
"members.\n"
"\n"
"*/\n"
"\n");
}

/********************************************************************/

/* printCppNames

Returned Value: none

Called By: printCppClasses

This prints in the C++ header file the declaration of all the classes.

*/

void printCppNames(                     /* ARGUMENTS                   */
 char * baseClassName,                  /* base class name             */
 classData * classDatas[26][CLASSSIZE], /* array of classDatas         */
 FILE * cppHFile)                       /* C++ header file to print in */
{
  int n;
  int k;
  
  for (n = 0; n < 26; n++)
    { // print declarations for all the classes
      for (k=0; classDatas[n][k]; k++)
	{
	  fprintf(cppHFile, "class %s;\n", classDatas[n][k]->name);
	}
    }
  fprintf(cppHFile, "class %s;\n", baseClassName);
  fprintf(cppHFile, "class dmisStatement;\n");
}

/********************************************************************/

/* printCppProductionClasses

Returned Value: none

Called By: printCppClasses

This prints one or more C++ classes to represent a single production.
While doing so, it adds to the classData for each class. If a class has
attributes, the atts of of the classData are set by makeClassDataAtts,
which is called in printCppClassTop and printCppClassDerived (which
are called here).  If a class has subclasses, the subs of the
classData are set by makeClassDataSubs, which is called here.

findClassData never returns a null pointer (it exits instead), so it
is not necessary to check here. Also, makeClassDataSubs checks that
there is at least one definition, so it is not necessary to check here.

*/

void printCppProductionClasses( /* ARGUMENTS                             */
 production * prod,             /* production for which to print classes */
 char * baseClassName,          /* name of base class                    */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists     */
 FILE * cppHFile,               /* C++ header file to print in           */
 FILE * cppCFile)               /* C++ code file to print in             */
{
  char * prodName;              // name of production
  defListCell * defCell;
  classData * data;             // classData for class
  
  makeClassDataSubs(prod, classDatas);
  prodName = prod->lhs;
  defCell = prod->defs->first;
  if (prod->isSupertype == true)
    {
      printCppClassParent(prodName, &(prod->subtypeOf),
			  baseClassName, cppHFile, cppCFile);
    }
  else if (defCell->next == 0)
    { // only one definition
      data = findClassData(prodName, classDatas);
      printCppClassTop(prodName, &(prod->subtypeOf), baseClassName,
		       defCell->data, data, cppHFile, cppCFile);
    }
  else
    { // two or more definitions; first print a class for the production
      printCppClassParent(prodName, &(prod->subtypeOf),
			  baseClassName, cppHFile, cppCFile);
      for ( ; defCell; defCell = defCell->next)
	{ // print a class for the rest of the definitions
	  data = findClassData(defCell->data->className, classDatas);
 	  printCppClassDerived(prod, prodName, defCell->data,
			       data, cppHFile, cppCFile);
	}
    }
}

/********************************************************************/

/* printLex

Returned Value: none

Called By: main

This:
1. opens the lex file for printing
2. calls printLexStart to print the first few lines of the file,
3. calls printLexMiddle to print some #defines, a few declarations,
   some class definitions for handling scopes, and some functions
   for dealing with variables and line numbers.
4. calls printLexToken repeatedly to print lex rules to handle all the
   DMIS words,
5. calls printLexEnd to print lex rules to handle all other token types.

*/

void printLex(        /* ARGUMENTS                   */
 char * baseFileName) /* base name for Lex file name */
{
  int m;
  int n;
  char * lexFileName;
  FILE * lexFile;

  lexFileName = new char[strlen(baseFileName) + 5];
  sprintf(lexFileName, "%s.lex", baseFileName);
  lexFile = fopen(lexFileName, "w");
  if (lexFile == 0)
    {
      fprintf(stderr, "Unable to open file %s for writing\n", lexFileName);
      exit(1);
    }
  printLexStart(baseFileName, lexFile);
  printLexMiddle(lexFile);
  for (n = 0; n < 26; n++)
    {
      for (m = 0; ((m < LETTERSIZE) && (tokenNames[n][m])); m++)
	{
	  printLexToken(tokenNames[n][m], tokenLexes[n][m], lexFile);
	}
    }
  printLexEnd(lexFile);
  fclose(lexFile);
  delete[] lexFileName;
}

/********************************************************************/

/* printLexEnd

Returned Value: none

Called By: printLex

This is called if the EBNF file has DECL and MACRO

This prints the end of the lex file. This includes all the rules for
reading character strings, labels, single characters, integer strings,
real strings, MACROs, DMIS/OFF, and DMIS/ON.

In the printed lex file, the right sides below line up correctly
since the backslashes on the left do not print.

The lexer that is built effectively executes DMIS/OFF and DMIS/ON
since when DMIS/OFF is read, the lexer just keeps reading until it
encounters a DMIS/ON.

The lexer that is built is checking for <CR><LF> at the end of each
line. An error is signaled by returning BAD if <LF> is found not
preceded by <CR>.

The lexer that is built expects to be reading a preprocessed
DMIS file. In a preprocessed DMIS file, every line starts with a line
number, and there are no line continuations.

*/

void printLexEnd( /* ARGUMENTS            */
 FILE * lexFile)  /* lex file to print in */
{
  fprintf(lexFile,
"{_}'                                    {ECH; j=0; BEGIN INSTRING;}\n" 
"<INSTRING>('')                          {ECH; stringText[j++] = '\\'';}\n" 
"<INSTRING>'{_}                          {ECH; BEGIN INITIAL;\n"
"                                         stringText[j] = 0;\n"
"                                         yylval.sval = strdup(stringText);\n"
"                                         return CHARSTRING;}\n"
"<INSTRING>[ -&(-~\\t]                    {ECH; stringText[j++]=yytext[0];}\n"
"<INSTRING>\\n                            {ECH;\n"
"                                         sprintf(lexMessage,\n"
"                                                 \"newline in string\");\n"
"                                         BEGIN INITIAL;\n"
"                                         return BAD;}\n"
"<INSTRING>.                             {ECH;\n"
"                                         sprintf(lexMessage,\n"
"                                              \"bad character in string\");\n"
"                                         BEGIN INITIAL;\n"
"                                         return BAD;}\n"
"<READLABEL>{_}[-a-zA-Z0-9._]{1,64}{_}   {ECH; BEGIN INITIAL;\n"
"                                         shiftUpcase(yytext);\n"
"                                         yylval.sval = strdup(yytext);\n"
"                                         return LBLNAME;}\n"
"<READLABEL>{_}\"@\"{_}                    {ECH; BEGIN INITIAL;\n"
"                                         return AT;}\n"
"<READLABEL>{_}\"/\"{_}                    {ECH; return SLASH;}\n"
"<READLABEL>{_}\"(\"{_}                    {ECH; return LPAREN;}\n"
"<READLABEL>{_}\")\"{_}                    {ECH; return RPAREN;}\n"
"<READLABEL>{_}\",\"{_}                    {ECH; return C;}\n"
"<READLABEL>\\n                           {ECH;\n"
"                                         sprintf(lexMessage,\n"
"                                                 \"label missing\");\n"
"                                         BEGIN INITIAL;\n"
"                                         return BAD;}\n"
"<READLABEL>.                            {ECH;\n"
"                                         sprintf(lexMessage,\n"
"                                               \"bad character in label\");\n"
"                                         BEGIN INITIAL;\n"
"                                         return BAD;}\n"
"<READ2>{_}[-a-zA-Z0-9._]{1,64}{_}       {ECH; BEGIN READLABEL;\n"
"                                         shiftUpcase(yytext);\n"
"                                         yylval.sval = strdup(yytext);\n"
"                                         return LBLNAME;}\n"
"<READ2>{_}\"@\"{_}                        {ECH; BEGIN AT2;\n"
"                                         return AT;}\n"
"<READ2>{_}\"/\"{_}                        {ECH; return SLASH;}\n"
"<READ2>{_}\"(\"{_}                        {ECH; return LPAREN;}\n"
"<READ2>\\n                               {ECH;\n"
"                                         sprintf(lexMessage,\n"
"                                                 \"label missing\");\n"
"                                         BEGIN INITIAL;\n"
"                                         return BAD;}\n"
"<READ2>.                                {ECH;\n"
"                                         sprintf(lexMessage,\n"
"                                            \"bad character in label\");\n"
"                                         BEGIN INITIAL;\n"
"                                         return BAD;}\n"
"<AT2>{_}[a-zA-Z][a-zA-Z0-9_]{0,15}{_}  {ECH;\n"
"                                    shiftUpcase(yytext);\n"
"                                    yylval.sval = strdup(yytext);\n"
"                                    if (findVariable(yytext, &varType, 0))\n"
"                                      {\n"
"                                        BEGIN READLABEL;\n"
"                                        return varType;\n"
"                                      }\n"
"                                    else\n"
"                                      {\n"
"                                        sprintf(lexMessage,\n"
"                                                \"undefined variable\");\n"
"                                        BEGIN INITIAL;\n"
"                                        return BAD;\n"
"                                      }\n"
"                                    }\n"
"<AT2>\\n                            {ECH;\n"
"                                    sprintf(lexMessage,\n"
"                                            \"variable missing\");\n"
"                                    BEGIN INITIAL;\n"
"                                    return BAD;}\n"
"<AT2>.                             {ECH;\n"
"                                    sprintf(lexMessage,\n"
"                                            \"bad character in variable\");\n"
"                                    BEGIN INITIAL;\n"
"                                    return BAD;}\n"
"<CALLARGS>[^\\r]*                   {ECH;\n"
"                                    if (closeUpcase(yytext))\n"
"                                      {\n"
"                                        sprintf(lexMessage,\n"
"                                                \"unclosed string\");\n"
"                                        return BAD;\n"
"                                      }\n"
"                                    yylval.sval = strdup(yytext);\n"
"                                    return CHARSTRING;}\n"
"<CALLARGS>\\r\\n                     {ECH; getCallArgs = 0;\n"
"                                    BEGIN INITIAL;\n"
"                                    return ENDLINE;}\n"
"<MACROIN>\"/\"                       {ECH;\n"
"                                    return SLASH;}\n"
"<MACROIN>{_}\",\"{_}                 {ECH; return C;}\n"
"<MACROIN>{_}[a-zA-Z][a-zA-Z0-9_]{0,15}{_}  {ECH;\n"
"                                   shiftUpcase(yytext);\n"
"                                   yylval.sval = strdup(yytext);\n"
"                                   if (findVariable(yytext, &varType, 1))\n"
"                                     {\n"
"                                       sprintf(lexMessage,\n"
"                                               \"argument reused\");\n"
"                                       return BAD;\n"
"                                     }\n"
"                                   else\n"
"                                     {\n"
"                                       recordVariable(yytext, MACROVARNAME);\n"
"                                       return MACROVARNAME;\n"
"                                     }\n"
"                                   }\n"
"<MACROIN>{_}'[a-zA-Z][a-zA-Z0-9_]{0,15}'{_}  {ECH; // remove quotes\n"
"                                   yytext[yyleng-1] = 0;\n"
"                                   yylval.sval = strdup(yytext+1);\n"
"				   strcpy(stringText, (yytext+1));\n"
"				   shiftUpcase(stringText);\n"
"                                   if (findVariable(stringText, &varType, 1))\n"
"                                     {\n"
"                                       sprintf(lexMessage,\n"
"                                               \"argument reused\");\n"
"                                       return BAD;\n"
"                                     }\n"
"                                   else\n"
"                                     {\n"
"                                       recordVariable(stringText,\n"
"                                                      MACROVARNAME);\n"
"                                       return CHARSTRING;\n"
"                                     }\n"
"                                   }\n"
"<MACROIN>\\r\\n                      {ECH;\n"
"                                    BEGIN MACROLINENUM;\n"
"                                    return ENDLINE;}\n"
"<MACROLINE>{_}{E}{N}{D}{M}{A}{C}{_} {ECH;\n"
"                                     if (scopeStack.next) scopeStack.pop();\n"
"                                     BEGIN INITIAL; return ENDMAC;}\n"
"<MACROLINE>{_}{E}{N}{D}{F}{I}{L}{_} {ECH;\n"
"                                     if (scopeStack.next) scopeStack.pop();\n"
"                                     BEGIN INITIAL; return ENDFIL;}\n"
"<MACROLINE>[^\\r]*                  {ECH;\n"
"                                    if (closeUpcase(yytext))\n"
"                                      {\n"
"                                        sprintf(lexMessage,\n"
"                                                \"unclosed string\");\n"
"                                        return BAD;\n"
"                                      }\n"
"                                    yylval.sval = strdup(yytext);\n"
"                                    return CHARSTRING;}\n"
"<MACROLINE>\\r\\n                    {ECH;\n"
"                                    BEGIN MACROLINENUM;\n"
"                                    return ENDLINE;}\n"
"<MACROLINENUM>^[0-9]+\"|\"           {lineTextIndex=0;\n"
"                                    sscanf(yytext,\"%%d\",&lineNo);\n"
"                                    j = 0;\n"
"                                    BEGIN MACROLINE;}\n"
"<DMISOFFLINENUM>^[0-9]+\"|\"         {lineTextIndex=0;\n"
"                                    sscanf(yytext,\"%%d\",&lineNo);\n"
"                                    j = 0;\n"
"                                    BEGIN DMISOFFLINE;}\n"
"<DMISOFFLINE>{_}{D}{M}{I}{S}{_}/(\"/\"{_}{O}{N}{_}\\r\\n) {ECH;\n"
"                                    BEGIN INITIAL;\n"
"                                    return DMIS;}\n"
"<DMISOFFLINE>{_}{E}{N}{D}{F}{I}{L}{_} {ECH;\n"
"                                    BEGIN INITIAL;\n"
"                                    return ENDFIL;}\n"
"<DMISOFFLINE>[^\\r]*                {ECH; yylval.sval = strdup(yytext);\n"
"                                    return CHARSTRING;}\n"
"<DMISOFFLINE>\\r\\n                  {ECH;\n"
"                                    BEGIN DMISOFFLINENUM;\n"
"                                    return ENDLINE;}\n"
"<DMISOFFIN>\"/\"                     {ECH;\n"
"                                    return SLASH;}\n"
"<DMISOFFIN>{_}{O}{F}{F}{_}         {ECH;\n"
"                                    return OFF;}\n"
"<DMISOFFIN>\\r\\n                    {ECH;\n"
"                                    BEGIN DMISOFFLINENUM;\n"
"                                    return ENDLINE;}\n"
"{_}{D}{M}{I}{S}{_}/(\"/\"{_}{O}{F}{F}{_}\\r\\n) {ECH; BEGIN DMISOFFIN;\n"
"                                             return DMIS;}\n"
"^[0-9]+\"|$$Next macro is a dummy - qwerty\"\\r\\n { macroIsReal = 0; }\n"
"^[0-9]+\"|\"{_}\"(\"{_}                     {doLineNo(); ECH;\n"
"                                         BEGIN READLABEL;\n"
"                                         return LPAREN;}\n"
"^[0-9]+\"|\"                              {lineTextIndex=0;\n"
"                                           sscanf(yytext,\"%%d\",&lineNo);}\n"
"{_}\"@\"{_}                               {ECH; return AT;}\n"
"{_}\",\"{_}                               {ECH; return C;}\n"
"{_}\":\"{_}                               {ECH; return COLON;}\n"
"{_}\"=\"{_}                               {ECH; return EQUALS;}\n"
"{_}\"[\"{_}                               {ECH; return LBOX;}\n"
"{_}\"(\"{_}                               {ECH; return LPAREN;}\n"
"{_}\"-\"{_}                               {ECH; return MINUS;}\n"
"{_}\"+\"{_}                               {ECH; return PLUS;}\n"
"{_}\"]\"{_}                               {ECH; return RBOX;}\n"
"{_}\")\"{_}                               {ECH; return RPAREN;}\n"
"{_}\"**\"{_}                              {ECH; return POWER;}\n"
"{_}\"*\"{_}                               {ECH; return TIMES;}\n"
"{_}\"/\"{_}                               {ECH; return SLASH;}\n"
"{_}\\r\\n                                 {ECH; BEGIN INITIAL;\n"
"                                         return ENDLINE;}\n"
"{_}\\n                                   {ECH;\n"
"                                         sprintf(lexMessage,\n"
"                                                 \"<CR> missing\");\n"
"                                         BEGIN INITIAL;\n"
"                                         return BAD;}\n"
"{_}[0-9]+{_}                            {ECH;\n"
"                                         sscanf(yytext, \"%%d\", &k);\n"
"                                         yylval.ival = k;\n"
"                                         return INTSTRING;}\n"
"{_}(([0-9]+\".\"[0-9]+)|(\".\"[0-9]+)){_}   {ECH;\n"
"                                         sscanf(yytext, \"%%lf\", &num);\n"
"                                         yylval.rval = num;\n"
"                                         return REALSTRING;}\n"
"{_}([0-9]+\".\")/[^a-zA-Z]{_}             {ECH;\n"
"                                         sscanf(yytext, \"%%lf\", &num);\n"
"                                         yylval.rval = num;\n"
"                                         return REALSTRING;}\n"
"{_}[a-zA-Z][a-zA-Z0-9_]{0,15}{_}  {ECH;\n"
"                                   shiftUpcase(yytext);\n"
"                                   yylval.sval = strdup(yytext);\n"
"                                   if (inDecl)\n"
"                                     {\n"
"                                       if (varType == -1)\n"
"                                         {\n"
"                                           inDecl = 0;\n"
"                                           sprintf(lexMessage,\n"
"                                                   \"bad variable type\");\n"
"                                           return BAD;\n"
"                                         }\n"
"                                       else if (findVariable(yytext,\n"
"                                                             &varType, 1))\n"
"                                         {\n"
"                                           sprintf(lexWarning,\n"
"                                                   \"variable redeclared?\");\n"
"                                           return DECLVARNAME;\n"
"                                         }\n"
"                                       else\n"
"                                         {\n"
"                                           recordVariable(yytext, varType);\n"
"                                           return DECLVARNAME;\n"
"                                         }\n"
"                                     }\n"
"                                   else // if (not inDecl)\n"
"                                     {\n"
"                                       if (findVariable(yytext,&varType,0))\n"
"                                         {\n"
"                                           return varType;\n"
"                                         }\n"
"                                       else\n"
"                                         {\n"
"                                           sprintf(lexMessage,\n"
"                                                   \"undefined variable\");\n"
"                                           return BAD;\n"
"                                         }\n"
"                                     }\n"
"                                  }\n"
".                                 {ECH;\n"
"                                   sprintf(lexMessage, \"bad character\");\n"
"                                   BEGIN INITIAL;\n"
"                                   return BAD;}\n"
"\n"
"%%%%\n"
"\n"
"int yywrap()\n"
"{\n"
"  return 1;\n"
"}\n");
}

/********************************************************************/

/* printLexMiddle

Returned Value: none

Called By: printLex

This prints a few #defines, some declarations, classes for dealing
with scope, and functions for dealing with scope and line numbers.
It also prints lex definitions and states.

Three C++ classes are defined to support scopes: varAndType, scope,
and scopeList. A varAndType records information about a single
variable. The information consists of the name of the variable and its
type.  A scope is an array of varAndType. A scopeList is a list of
scopes.

The scopeStack in the parser (which is a scopeList) always consists of
at least one scope, which is the scope for the main DMIS program. When
a CALL definition is encountered, a new scope is pushed on top of the
scopeStack, so there will be two scopes on the scopeStack, the top one
of which is the scope of the CALL. When a CALL ends (with an ENDMAC),
the topmost scope is popped off the scopeStack.

Since ENDMAC causes the stack to be popped, and a macroBlock ends
with ENDMAC, a new scope is also pushed on top of the scopeStack
when a MACRO definition is encountered. However, nothing gets put
into the scope at the top of the stack when a macroBlock is read.

When the parser encounters a DECL, it adds the variables declared
in the DECL to the topmost scope.

The rules for variable scopes are:
1. In any scope, the same variable name may not be used twice.
2. A variable in one scope may have the same name as a variable
   in any lower scope.
3. When a variable is referenced, it must occur in at least one
   scope, and is assumed to refer to the varAndType of the given
   name in the scope nearest the top of the scopeStack in which
   a variable of that name may be found.

The findVariable function this writes hunts through the scopeStack to
find a variable. The topOnly argument to that function is set to 1 for
checking whether a DECL variable is already defined at the topmost
scope. The argument is set to 0 for finding a referenced variable.

The recordVariable function this writes records a variable in the
topmost scope.

The doLineNo function this writes records the current line number in
the lineNo global variable. That number occurs at the beginning of the
line being read. The function then shifts the original text of the
line (the text that was there before the preprocessor added a line
number at the beginning) to the left of the yytext array, erasing the
line number. This is done so that the line number will not appear in
the text of the line being read (which is saved in the lineText
array by ECH).

*/

void printLexMiddle( /* ARGUMENTS            */
 FILE * lexFile)     /* lex file to print in */
{
  fprintf(lexFile,
"\n"
"#define ECH  for (k=0; ((k < yyleng) && (lineTextIndex < 4095));)\\\n"
"    lineText[lineTextIndex++] = yytext[k++];\\\n"
"    lineText[lineTextIndex] = 0\n"
"\n"
"#define VARLIST_MAX_SIZE 1024\n"
"\n"
"extern int resetLex;\n"
"extern int macroIsReal;\n"
"extern int getCallArgs;\n"
"extern char lineText[];\n"
"extern char lexMessage[];\n"
"extern char lexWarning[];\n"
"extern int lineNo;\n"
"extern int inDecl;\n"
"int lineTextIndex;\n"
"char stringText[4096];\n"
"int j;      // index for stringText\n"
"double num; // number to parse reals into\n"
"int k;      // utility index, used in ECH compiler macro\n"
"int varType;\n"
"// scopeList scopeStack; is declared below\n"
"\n"
"class varAndType {\n"
" public:\n"
"  varAndType(){}\n"
"  ~varAndType(){}\n"
"  char * name;\n"
"  int theType;\n"
"};\n"
"\n"
"class scope {\n"
" public:\n"
"  scope(){size = 0;}\n"
"  ~scope(){}\n"
"  varAndType vars[VARLIST_MAX_SIZE];\n"
"  int size;\n"
"};\n"
"\n"
"class scopeList {\n"
" public:\n"
"  scopeList(){data = new scope; next = 0;}\n"
"  ~scopeList(){};\n"
"  void push(scope * addIt)\n"
"    {\n"
"      scopeList * newbie;\n"
"      newbie = new scopeList;\n"
"      newbie->next = next;\n"
"      newbie->data = data;\n"
"      next = newbie;\n"
"      data = addIt;\n"
"    }\n"
"  void pop()\n"
"    { // don't pop a list with only one element\n"
"      scopeList * temp;\n"
"      temp = next;\n"
"      delete data;\n"
"      data = next->data;\n"
"      next = next->next;\n"
"      delete temp;\n"
"    }\n"
"  scope * data;\n"
"  scopeList * next;\n"
"};\n"
"\n"
"scopeList scopeStack;\n"
"\n"
"int findVariable(\n"
" char * varName,\n"
" int * tipe,\n"
" int topOnly)\n"
"{\n"
"  int n;\n"
"  scopeList * tier;\n"
"  scope * aScope;\n"
"\n"
"  for (tier = &scopeStack; tier; tier = tier->next)\n"
"    {\n"
"      aScope = tier->data;\n"
"      for (n = 0; n < aScope->size; n++)\n"
"        {\n"
"          if (strcmp(varName, aScope->vars[n].name) == 0)\n"
"            {\n"
"              *tipe = aScope->vars[n].theType;\n"
"              return (n+1);\n"
"            }\n"
"        }\n"
"      if (topOnly)\n"
"        break;\n"
"    }\n"
"  return 0;\n"
"}\n"
"\n"
"void doLineNo()\n"
"{\n"
"  int n;\n"
"  int m;\n"
"\n"
"  sscanf(yytext, \"%%d%%n\", &lineNo, &n);\n"
"  n++;\n"
"  for (m = n; m < yyleng; m++)\n"
"    yytext[m-n] = yytext[m];\n"
"  yytext[m-n] = 0;\n"
"  yyleng = (yyleng - n);\n"
"  lineTextIndex = 0;\n"
"}\n"
"\n"
"void recordVariable(\n"
" char * varName,\n"
" int varType)\n"
"{\n"
"  scope * aScope;\n"
"\n"
"  aScope = scopeStack.data;\n"
"  if (aScope->size >= VARLIST_MAX_SIZE)\n"
"    {\n"
"      fprintf(stderr, \"Quitting: too many variables\\n\");\n"
"      exit (1);\n"
"    }\n"
"  aScope->vars[aScope->size].name = strdup(varName);\n"
"  aScope->vars[aScope->size].theType = varType;\n"
"  aScope->size++;\n"
"}\n"
"\n"
"void shiftUpcase(\n"
" char * text)\n"
"{\n"
"  int n;\n"
"  int first;\n"
"  char c;\n"
"\n"
"  for (first = 0; text[first] <= ' '; first++);\n"
"  for (n = first; text[n] > ' '; n++)\n"
"    {\n"
"      c = text[n];\n"
"      text[n - first] = (islower(c) ? toupper(c) : c);\n"
"    }\n"
"  text[n - first] = 0;\n"
"}\n"
"\n"
"int closeUpcase(\n"
" char * text)\n"
"{\n"
"  int m;         // old text index\n"
"  int n;         // new text index\n"
"  int inString;\n"
"  char c;\n"
"\n"
"  inString = 0;\n"
"  for (n = 0, m = 0; ((c = text[m]) != 0); m++)\n"
"    {\n"
"      if (inString)\n"
"        {\n"
"          text[n++] = c;\n"
"          if (c == '\\'')\n"
"            {\n"
"	      if (text[m+1] == '\\'')\n"
"		{ // two quotes in a row; keep both\n"
"		  m++;\n"
"		  text[n++] = '\\'';\n"
"		}\n"
"	      else\n"
"		inString = 0;\n"
"            }\n"
"        }\n"
"      else if ((c == ' ') || (c == '\\t')); // don't copy blank or tab\n"
"      else if (c == '\\'')\n"
"        { // string is starting\n"
"          inString = 1;\n"
"          text[n++] = c;\n"
"        }\n"
"      else\n"
"        {\n"
"          text[n++] = (islower(c) ? toupper(c) : c);\n"
"        }\n"
"    }\n"
"  text[n] = 0;\n"
"  return ((inString) ? 1 : 0);\n"
"}\n"
"\n"
"void emptyScopeStack()\n"
"{\n"
"  int n;\n"
"\n"
"  for (n = 0; n < scopeStack.data->size; n++)\n"
"    delete scopeStack.data->vars[n].name;\n"
"  scopeStack.data->size = 0;\n"
"  if (scopeStack.next)\n"
"    sprintf(lexMessage, \"ENDMAC missing\");\n"
"}\n"
"\n"
"%%}\n"
"\n"
"A [aA]\n"
"B [bB]\n"
"C [cC]\n"
"D [dD]\n"
"E [eE]\n"
"F [fF]\n"
"G [gG]\n"
"H [hH]\n"
"I [iI]\n"
"J [jJ]\n"
"K [kK]\n"
"L [lL]\n"
"M [mM]\n"
"N [nN]\n"
"O [oO]\n"
"P [pP]\n"
"Q [qQ]\n"
"R [rR]\n"
"S [sS]\n"
"T [tT]\n"
"U [uU]\n"
"V [vV]\n"
"W [wW]\n"
"X [xX]\n"
"Y [yY]\n"
"Z [zZ]\n"
"\n"
"_ [ \\t]*\n"
"\n"
"%%x INSTRING\n"
"%%x READLABEL\n"
"%%x READ2\n"
"%%x AT2\n"
"%%x DMISOFFIN\n"
"%%x DMISOFFLINENUM\n"
"%%x DMISOFFLINE\n"
"%%x CALLARGS\n"
"%%x MACROIN\n"
"%%x MACROLINE\n"
"%%x MACROLINENUM\n"
"\n"
"%%%%\n"
"\n"
"   if (resetLex)\n"
"     {\n"
"       while (scopeStack.next)\n"
"	 scopeStack.pop();\n"
"       BEGIN INITIAL;\n"
"       resetLex = 0;\n"
"     }\n"
"   else if (getCallArgs)\n"
"     BEGIN CALLARGS;\n"
"\n");
}

/********************************************************************/

/* printLexStart

Returned Value: none

Called By:
  printLex

This prints the beginning of the lex file for the parser. It
prints the standard NIST disclaimer, some suggestions for
documentation to read, and four #includes.

*/

void printLexStart(   /* ARGUMENTS                   */
 char * baseFileName, /* base name for Lex file name */
 FILE * lexFile)      /* lex file to print in        */
{
  fprintf(lexFile,
"%%{\n"
"\n"
"/************************************************************************\n"
"  DISCLAIMER:\n"
"  This software was produced by the National Institute of Standards\n"
"  and Technology (NIST), an agency of the U.S. government, and by statute\n"
"  is not subject to copyright in the United States.  Recipients of this\n"
"  software assume all responsibility associated with its operation,\n"
"  modification, maintenance, and subsequent redistribution.\n"
"\n"
"  See NIST Administration Manual 4.09.07 b and Appendix I.\n"
"************************************************************************/\n"
"/*\n"
"See DMIS Sec. 5.1.1 and Sec. 5.1.2.7 regarding variable names\n"
"See DMIS Sec. 5.1.1 and Sec. 5.1.2.4 regarding label names\n"
"See DMIS Sec. 5.1.1 and Sec. 5.1.2.5 regarding text strings\n"
"*/\n"
"\n"
"#include <string.h>          // for strdup, etc.\n"
"#include <ctype.h>           // for isalpha\n"
"#include \"%s.hh\"\n", baseFileName);
  fprintf(lexFile,
"#include \"%sYACC.hh\"\n", baseFileName);
}

/********************************************************************/

/* printLexString

Returned Value: none

Called By:
  printLexToken

This prints the lex rule for theName, plus the beginning of the action
for the rule. The rule is always of the form:
(optional white space) leader name trailer

When the parser that is built executes the "ECH" action written here, it
inserts the text that is read at the end of the parser's lineText array.
ECH is defined by the printLexMiddle function.

*/

void printLexString(     /* ARGUMENTS                          */
 const char * leader,    /* text to print before printing name */
 const char * lexString, /* lex regular expression to print    */
 const char * trailer,   /* text to print after printing name  */
 FILE * lexFile)         /* lex file to print in               */
{
  int j;

  j = fprintf(lexFile, "{_}%s%s%s", leader, lexString, trailer);
  for (; j < 40; j++)
    fputc(' ', lexFile);
  fprintf(lexFile, "{ECH; ");
}

/********************************************************************/

/* printLexToken

Returned Value: none

Called By: printLex

A. Label names

Label names have the form of characters in parentheses:
    (this_isALabel)
In this discussion, the characters inside the parentheses are called
a label.
 
Deciding when the DMIS parser should look for a label is not easy
since reserved words, numbers, and variable names may be used as
labels and parentheses are used in expressions. The following methods
use various cues in the DMIS language.  The methods use lex exclusive
states that are entered when it is time to look for a label and exited
when either a label is read or the end of the line is reached. The
state READLABEL is used most.

When the lexer is in the READLABEL state, a label name is the first
thing the lexer looks for. If an @ is read while in the READLABEL
state, the INITIAL state is entered, since that means the name of a
string variable should come next.

No communication between Lex and YACC is needed.

A1. labelWords1

Many words are either variable names or label types. The usage can be
distinguished because label types are always followed by a left
parenthesis and then a label, while variable names are never followed
by a left parenthesis. These words are given in the labelWords1
array. Whenever one of these words is encountered followed by a left
parenthesis, the READLABEL state is entered, and the token whose
spelling is the same as that of the word is returned. If one of these
words is not followed by a parenthesis, it will be treated as a
variable name. White space between a label type and the left
parenthesis is allowed here.

A2. labelWords2

Two tokens (ERROR and RESUME) that are always followed by a slash may
or may not have a label name in the next position. These tokens are
given in the labelWords2 array. This function writes the lex so that
the READLABEL state is entered if the token is followed by a slash and
then a left parentheses.  For each of these words, the function writes
two expressions to look for, one with a label name following, and one
without.

A3. labelWords3

Three tokens are reserved words that are followed soon by a left
parenthesis and then a label. DATTRG is always followed immediately by
a label. JUMPTO is always followed by a slash followed by a left
parenthesis and then a label. QUERY is always followed by a comma
followed by a left parenthesis and then a label.  These tokens are
given in the labelWords3 array. Whenever one of these words is
encountered, the READLABEL state is entered.

A4. Jump labels

A jump label is identified by a line starting with a label name.  This
function writes the lex so that the READLABEL state is entered if the
start of a line is followed by a left parenthesis.

A5. ITERAT

ITERAT is always followed by a slash and then two jump labels separated
by a comma. To deal with this, when ITERAT is read, the READ2 state
is entered. When in the READ2 state, after reading a label, the
READLABEL state is entered. If an @ is read while in the READ2
state, the AT2 state is entered in which a variable name is
looked for, and then the READLABEL state is entered.

This handles the scope of variables. See documentation of printLexMiddle.

Since this is intended for handling subsets of DMIS, it may be that
not all the labelWords1, labelWords2, and labelWords3 are among the
tokenNames.  This is handling the case that not all are used. All
these sets of words and the tokenNames must be in alphabetical order,
however, for the processing to work correctly.

B. CALL, MACRO, and ENDMAC

See documentation of printLexMiddle.

This writes actions for the lex rules for CALL, MACRO, ENDMAC, and
ENDFIL that deal with scope. The actions that are written are:
1. A new scope is pushed on the scopeStack when CALL or MACRO is read.
2. The top scope is popped off the scopeStack when an ENDMAC is reached,
   unless there is only one scope left (implying an error has occurred,
   and there was no preceding CALL or MACRO to match the ENDMAC).
3. When ENDFIL is read, a check is made that there is only one scope
   on the scopeStack, and an error is signaled if there is more than
   one, since this implies a MACRO was not ended by a matching ENDMAC.
   Also, all variables in the one scope are deleted; this is necessary
   to prepare for reading another DMIS file.

C. COMMA

An upper case C is used to in the YACC output to stand for a comma, so
C must appear as a token in the YACC file. An upper case C in a DMIS
file, however (which could be a variable name) should not be taken to
mean the token C. Hence, when theName is "C" in this function, nothing
is printed.

D. Variable types

The varType is set for BOOL, CHAR, DECL, DOUBLE, INTGR, LONG, REAL,
and VECTOR.

*/

void printLexToken( /* ARGUMENTS            */
 char * theName,    /* token name to print  */
 char * lexName,    /* lex name to look for */
 FILE * lexFile)    /* lex file to print in */
{
  char lexString[NAMESIZE]; // lex regular expression to print
  int m;                    // index for lexName
  int n;                    // index for lexString
  static int i = 0;         // index for labelWords1
  static int j = 0;         // index for labelWords2
  static int k = 0;         // index for labelWords3
  static const char * labelWords1[] = {
    "CC", "CI", "CR", "CS", "CZ",
    "D", "DA", "DAT", "DI", "DID", "DR", "DRA", "DS", "DV",
    "F", "FA", "FI", "FS", "G", "GSA", "KC", "KCA", "LI", "M", "MA", "MD",
    "OP", "P", "PC", "PL", "PN", "PR", "PS", "PV", "Q", "R", "RM", "RT",
    "S", "SA", "SE", "SG", "SGS", "SR", "SS", "ST", "SW", "SX",
    "T", "TA", "TH", "TL", "U", "V", "VA", "VF", "VL", "VW", "~theEnd"};
  static const char * labelWords2[] = {
    "ERROR", "RESUME", "~theEnd"};
  static const char * labelWords3[] = {
    "DATTRG", "JUMPTO", "QUERY", "~theEnd"};

  if (strcmp(theName, "C") == 0)
    return;
  n = 0;
  for (m = 0; lexName[m]; m++)
    {
      if ((lexName[m] >= 'A') && (lexName[m] <= 'Z'))
	n = (n + sprintf((lexString + n), "{%c}", lexName[m]));
      else
	n = (n + sprintf((lexString + n), "\"%c\"", lexName[m]));
    }
  for (; (strcmp(labelWords1[i], theName) < 0); i++);
  for (; (strcmp(labelWords2[j], theName) < 0); j++);
  for (; (strcmp(labelWords3[k], theName) < 0); k++);
  if (strcmp(theName, labelWords1[i]) == 0)
    {
      printLexString("(", lexString, "{_})/\"(\"", lexFile);
      fprintf(lexFile, "BEGIN READLABEL;\n"
	      "                                         ");
      i++;
    }
  else if (strcmp(theName, labelWords2[j]) == 0)
    { // need to print name twice
      printLexString("(", lexString, "{_})/\\/{_}\"(\" ", lexFile);
      fprintf(lexFile, "BEGIN READLABEL;\n"
	      "                                         "
	      "return %s;}\n", theName);
      printLexString("", lexString, "{_} ", lexFile);
      j++;
    }
  else if (strcmp(theName, labelWords3[k]) == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "BEGIN READLABEL;\n"
	      "                                         ");
      k++;
    }
  else if (strcmp(theName, "ITERAT") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "BEGIN READ2;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "ENDFIL") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile,
"\n"
"                                         emptyScopeStack();\n"
"                                         if (scopeStack.next)\n"
"                                           { scopeStack.next = 0;\n"
"                                             return BAD;\n"
"                                           }\n"
"                                         ");
    }
  else if (strcmp(theName, "ENDMAC") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile,
"\n"
"                                         if (!scopeStack.next)\n"
"                                           {sprintf(lexMessage,\n"
"                                                    \"ENDMAC misplaced\");\n"
"                                            return BAD;}\n"
"                                         scopeStack.pop();\n"
"                                         ");
    }
  else if (strcmp(theName, "MACRO") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "BEGIN MACROIN;\n"
"                                         scopeStack.push(new scope);\n"
"                                         ");
    }
  else if (strcmp(theName, "CALL") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "\n"
"                                         scopeStack.push(new scope);\n"
"                                         ");
    }
  else if (strcmp(theName, "BOOL") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "varType = BOOLVARNAME;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "CHAR") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "varType = STRINGVARNAME;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "DECL") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "varType = -1;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "DOUBLE") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "varType = REALVARNAME;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "INTGR") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "varType = INTVARNAME;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "LONG") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "varType = REALVARNAME;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "REAL") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "varType = REALVARNAME;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "VECTOR") == 0)
    {
      printLexString("", lexString, "{_} ", lexFile);
      fprintf(lexFile, "varType = VECTORVARNAME;\n"
	      "                                         ");
    }
  else if (strcmp(theName, "WWEOF") == 0)
    {
      printLexString("", "{E}{O}{F}", "{_} ", lexFile);
    }
  else
    {
      printLexString("", lexString, "{_} ", lexFile);
    }
  fprintf(lexFile, "return %s;}\n", theName);
}

/********************************************************************/

/*  printStarLine

Returned Value: none

Called By:
  printConfAttChecker
  printConfChecker
  printConfFunctions
  printConfSubChecker
  printCppClasses
  printCppClassStart
  printYaccStart

This prints a line of stars (preceded and followed by blank lines) to
separate sections of code visually.

*/

void printStarLine( /* ARGUMENTS        */
 FILE * someFile)   /* file to print in */
{
  fprintf(someFile,
"\n"
"/********************************************************************/\n"
"\n");
}

/********************************************************************/

/* printTestAttChecker

Returned Value: none

Called By: printTestFunctions

This prints the checking function for a class with attributes.  For
each attribute that is not null, it does the following:

A. If log is non-zero and the conformance class is not supposed to
have the attribute but the attribute is present, then this prints an
error message and does A1 or A2 or A3.

A1. If the attribute is a basic type (double, int, char *, or bool) or
a list of a basic type, then it does not need checking.

A2. If the attribute (1) is a list (but not a list of a basic type)
and (2) is of a type that has either subtypes or attributes, then this
calls the checking function for the type for each element of the list
with the log argument set to 0.

A3. If the attribute (1) is not a list and (2) is not a basic type and (3)
has either subtypes or attributes, then this calls the checking
function for the type for the attribute with the log argument set to 0.

B. Otherwise (either log is 0 or [log is non-zero and the attribute is
supposed to be there]), then B1 or B2 or B3 is done.

B1. same as A1

B2. same as A2, except the log argument is set as given in the arguments.

B3. same as A3, except the log argument is set as given in the arguments.

Example: check_vformStm is the function that is printed for the
vformStm class. a_vformStm may have two attributes: a_vLabel and
a_vformItemList. vformStmAtts is a list of all attributes allowed in
the conformance class for a vformStm.

void check_vformStm(
 vformStm * a_vformStm,
 int log)
{
  if (a_vformStm->get_vLabel())
    {
      adjustLevels(&vformStmAtts_a_vLabel, "vformStm a_vLabel");
      if (log && !findString("a_vLabel", vformStmAtts))
	{
	  warnAtt("a_vLabel", "vformStm", a_vformStm);
	  check_vLabel(a_vformStm->get_vLabel(), 0);
	}
      else
	check_vLabel(a_vformStm->get_vLabel(), log);
    }
  if (a_vformStm->get_vformItemList())
    {
      std::list<vformItem *> * theList;
      std::list<vformItem *>::iterator iter;
      theList = a_vformStm->get_vformItemList();
      adjustLevels(&vformStmAtts_a_vformItemList,
		   "vformStm a_vformItemList");
      if (log && !findString("a_vformItemList", vformStmAtts))
	{
	  warnAtt("a_vformItemList", "vformStm", a_vformStm);
	  for (iter = theList->begin(); iter != theList->end(); iter++)
	    check_vformItem(*iter, 0);
	}
      else
	{
	  for (iter = theList->begin(); iter != theList->end(); iter++)
	    check_vformItem(*iter, log);
	}
    }
}

This avoids lines that are too long (over 80 characters) by testing the
length of the items to be printed and going to a new line if necessary.

*/

void printTestAttChecker(               /*  ARGUMENTS                */
 attCell * aCell,                       /* attribute data            */
 char * className,                      /* name of class             */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * tester)                         /* file to print to          */
{
  char * attName;
  char * getName;
  char * typeName;
  bool isList;
  classData * attData;
  bool printIt;         // true means not char, int, double or bool
  int classLen;         // length of className
  int attLen;           // length of attName
  int getLen;           // length of getName
  int typeLen;          // length of typeName

  classLen = strlen(className);
  fprintf(tester, "void check_%s(\n", className);
  fprintf(tester, " %s * a_%s,\n", className, className);
  fprintf(tester, " int log)\n");
  fprintf(tester, "{\n");
  for (; aCell; aCell = aCell->next)
    {
      isList = ((aCell->typeSort == attCell::lst) ? true : false);
      attName = aCell->attName;
      attLen = strlen(attName);
      typeName = aCell->typeName;
      typeLen = strlen(typeName);
      printIt = (strcmp(typeName, "char") && strcmp(typeName, "int") &&
		 strcmp(typeName, "bool") && strcmp(typeName, "double"));
      getName = (((attName[0] == 'a') && (attName[1] == '_')) ?
		 (attName + 2) : attName);
      getLen = strlen(getName);
      fprintf(tester, "  if (a_%s->get_%s())\n", className, getName);
      fprintf(tester, "    {\n");
      if (printIt && isList)
	{
	  fprintf(tester, "      std::list<%s *> * theList;\n", typeName);
	  fprintf(tester, "      std::list<%s *>::iterator iter;\n", typeName);
	  fprintf(tester, "      theList = a_%s->get_%s();\n",
		  className, getName);
	}
      fprintf(tester, "      adjustLevels(&%sAtts_%s,", className, attName);
      if ((classLen + attLen) > 23)
	fprintf(tester, "\n\t\t  ");
      fprintf(tester, " \"%s %s\");\n", className, attName);
      fprintf(tester, "      if (log && !findString(\"%s\",", attName);
      if ((attLen + classLen) > 40)
	fprintf(tester, "\n\t\t\t    ");
      fprintf(tester, " %sAtts))\n", className);
      fprintf(tester, "\t{\n");
      fprintf(tester, "\t  warnAtt(\"%s\", \"%s\",", attName, className);
      if ((classLen + classLen + attLen) > 48)
	fprintf(tester, "\n\t\t ");
      fprintf(tester, " a_%s);\n", className);
      if (printIt && isList)
	{
	  fprintf(tester, "\t  for (iter = theList->begin(); "
		  "iter != theList->end(); iter++)\n");
	  fprintf(tester, "\t    check_%s(*iter, 0);\n", typeName);
	  fprintf(tester, "\t}\n");
	  fprintf(tester, "      else\n");
	  fprintf(tester, "\t{\n");
	  fprintf(tester, "\t  for (iter = theList->begin(); "
		  "iter != theList->end(); iter++)\n");
	  fprintf(tester, "\t    check_%s(*iter, log);\n", typeName);
	  fprintf(tester, "\t}\n");
	}
      else if (printIt)
	{
	  attData = findClassData(typeName, classDatas);
	  if ((attData->atts == 0) && (attData->subs == 0))
	    { // attribute class has neither attributes nor subtypes
	      fprintf(tester, "\t}\n");
	      fprintf(tester, "    }\n");
	      continue;
	    }
	  fprintf(tester, "\t  check_%s(", typeName);
	  if ((typeLen + classLen + getLen) > 48)
	    fprintf(tester, "\n\t    ");
	  fprintf(tester, "a_%s->get_%s(), 0);\n", className, getName);
	  fprintf(tester, "\t}\n");
	  fprintf(tester, "      else\n");
	  fprintf(tester, "\tcheck_%s(", typeName);
	  if ((typeLen + classLen + getLen) > 48)
	    fprintf(tester, "\n\t  ");
	  fprintf(tester, "a_%s->get_%s(), log);\n", className, getName);
	}
      else
	{
	  fprintf(tester, "\t}\n");
	}
      fprintf(tester, "    }\n");
    }
  fprintf(tester, "}\n");
  printStarLine(tester);
}

/********************************************************************/

/* printTestBlockAttChecker

Returned Value: none

Called By: printTestFunctions

This prints a function that checks an instance of a class that is
a block and has attributes.

This is very similar to printTestAttChecker except that the error
message printer (warnAttBlock) is different. Also, if an attribute is
a list that is not allowed, then only the first element of the list is
sent to the error message printer.

For example:

void check_doBlock(
 doBlock * a_doBlock,
 int log)
{
  if (a_doBlock->get_doStm())
    {
      adjustLevels(&doBlockAtts_a_doStm, "doBlock a_doStm");
      if (log && !findString("a_doStm", doBlockAtts))
	{
	  warnAttBlock("doStm", a_doBlock->get_doStm());
	  check_doStm(a_doBlock->get_doStm(), 0);
	}
      else
	check_doStm(a_doBlock->get_doStm(), log);
    }
  if (a_doBlock->get_dmisItemList())
    {
      std::list<dmisItem *> * theList;
      std::list<dmisItem *>::iterator iter;
      theList = a_doBlock->get_dmisItemList();
      adjustLevels(&doBlockAtts_a_dmisItemList,
		   "doBlock a_dmisItemList");
      if (log && !findString("a_dmisItemList", doBlockAtts))
	{
	  warnAttBlock("dmisItemList",
	    a_doBlock->get_dmisItemList()->front());
	  for (iter = theList->begin(); iter != theList->end(); iter++)
	    check_dmisItem(*iter, 0);
	}
      else
	{
	  for (iter = theList->begin(); iter != theList->end(); iter++)
	    check_dmisItem(*iter, log);
	}
    }
  if (a_doBlock->get_enddoStm())
    {
      adjustLevels(&doBlockAtts_a_enddoStm, "doBlock a_enddoStm");
      if (log && !findString("a_enddoStm", doBlockAtts))
	{
	  warnAttBlock("enddoStm", a_doBlock->get_enddoStm());
	}
    }
}

*/

void printTestBlockAttChecker(          /*  ARGUMENTS                */
 attCell * aCell,                       /* attribute data            */
 char * className,                      /* name of class             */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * tester)                        /* file to print to          */
{
  char * attName;
  char * getName;
  char * typeName;
  bool isList;
  classData * attData;
  bool printIt;           // true means not char, int, double or bool
  int classLen;         // length of className
  int attLen;           // length of attName
  int getLen;           // length of getName
  int typeLen;          // length of typeName

  classLen = strlen(className);
  fprintf(tester, "void check_%s(\n", className);
  fprintf(tester, " %s * a_%s,\n", className, className);
  fprintf(tester, " int log)\n");
  fprintf(tester, "{\n");
  for (; aCell; aCell = aCell->next)
    {
      isList = ((aCell->typeSort == attCell::lst) ? true : false);
      attName = aCell->attName;
      attLen = strlen(attName);
      typeName = aCell->typeName;
      typeLen = strlen(typeName);
      printIt = (strcmp(typeName, "char") && strcmp(typeName, "int") &&
		 strcmp(typeName, "bool") && strcmp(typeName, "double"));
      getName = (((attName[0] == 'a') && (attName[1] == '_')) ?
		 (attName + 2) : attName);
      getLen = strlen(getName);
      fprintf(tester, "  if (a_%s->get_%s())\n", className, getName);
      fprintf(tester, "    {\n");
      if (printIt && isList)
	{
	  fprintf(tester, "      std::list<%s *> * theList;\n", typeName);
	  fprintf(tester, "      std::list<%s *>::iterator iter;\n", typeName);
	  fprintf(tester, "      theList = a_%s->get_%s();\n",
		  className, getName);
	}
      fprintf(tester, "      adjustLevels(&%sAtts_%s,", className, attName);
      if ((classLen + attLen) > 23)
	fprintf(tester, "\n\t\t  ");
      fprintf(tester, " \"%s %s\");\n", className, attName);

      fprintf(tester, "      if (log && !findString(\"%s\",", attName);
      if ((attLen + classLen) > 40)
	fprintf(tester, "\n\t\t\t    ");
      fprintf(tester, " %sAtts))\n", className);
      fprintf(tester, "\t{\n");
      fprintf(tester, "\t  warnAttBlock(\"%s\",", getName);
      if (isList || ((classLen + getLen +getLen) > 38))
	fprintf(tester, "\n\t   ");
      fprintf(tester, " a_%s->get_%s()%s);\n",
	      className, getName, (isList ? "->front()" : ""));
      if (printIt && isList)
	{
	  fprintf(tester, "\t  for (iter = theList->begin(); "
		  "iter != theList->end(); iter++)\n");
	  fprintf(tester, "\t    check_%s(*iter, 0);\n", typeName);
	  fprintf(tester, "\t}\n");
	  fprintf(tester, "      else\n");
	  fprintf(tester, "\t{\n");
	  fprintf(tester, "\t  for (iter = theList->begin(); "
		  "iter != theList->end(); iter++)\n");
	  fprintf(tester, "\t    check_%s(*iter, log);\n", typeName);
	  fprintf(tester, "\t}\n");
	}
      else if (printIt)
	{
	  attData = findClassData(typeName, classDatas);
	  if ((attData->atts == 0) && (attData->subs == 0))
	    { // attribute class has neither attributes nor subtypes
	      fprintf(tester, "\t}\n");
	      fprintf(tester, "    }\n");
	      continue;
	    }
	  fprintf(tester, "\t  check_%s(", typeName);
	  if ((typeLen + classLen + getLen) > 48)
	    fprintf(tester, "\n\t    ");
	  fprintf(tester, "a_%s->get_%s(), 0);\n", className, getName);
	  fprintf(tester, "\t}\n");
	  fprintf(tester, "      else\n");
	  fprintf(tester, "\tcheck_%s(", typeName);
	  if ((typeLen + classLen + getLen) > 48)
	    fprintf(tester, "\n\t  ");
	  fprintf(tester, "a_%s->get_%s(), log);\n", className, getName);
	}
      fprintf(tester, "    }\n");
    }
  fprintf(tester, "}\n");
  printStarLine(tester);
}

/********************************************************************/

/* printTestBlockSubChecker

Returned Value: none

Called By: printTestFunctions

This prints a function that checks an instance of a class that is a
block and has subclasses. Every subclass of a class that is a block
should be allowed in every conformance class. For each class name in
the subs list, the function this prints checks whether the instance
belongs to the subclass. When subclass of the instance is found,
the function this writes checks if the subclass is not allowed and
prints a bug message indicating the conformance checker needs fixing
if the subclass is not allowed. If the subclass is allowed, the
function this prints casts the instance into that subclass and
calls the checker for the subclass.

For example:

void check_measBlock(
 measBlock * a_measBlock,
 int log)
{
  if (isA(a_measBlock, measBlock_measStm))
    {
      if (log && !findString("measBlock_measStm", measBlockSubs))
        {warnBlockBug("measBlock_measStm", "measBlock");}
      else
	check_measBlock_measStm(
	  dynamic_cast<measBlock_measStm *>(a_measBlock), log);
    }
  else if (isA(a_measBlock, measBlock_rmeasStm))
    {
      if (log && !findString("measBlock_rmeasStm", measBlockSubs))
        {warnBlockBug("measBlock_rmeasStm", "measBlock");}
      else
	check_measBlock_rmeasStm(
	  dynamic_cast<measBlock_rmeasStm *>(a_measBlock), log);
    }
}

*/

void printTestBlockSubChecker(          /* ARGUMENTS                 */
 stringCell * strCell,                  /* first cell in subs        */
 char * className,                      /* name of class             */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * tester)                        /* file to print to          */
{
  int printElse = 0;
  char * typeName;
  classData * subData;
  int classLen;         // length of className
  int typeLen;          // length of typeName

  classLen = strlen(className);
  fprintf(tester, "void check_%s(\n", className);
  fprintf(tester, " %s * a_%s,\n", className, className);
  fprintf(tester, " int log)\n");
  fprintf(tester, "{\n");
  for (; strCell; strCell = strCell->next)
    {
      typeName = strCell->data;
      typeLen = strlen(typeName);
      fprintf(tester, "  ");
      if (printElse)
	fprintf(tester, "else ");
      else
	printElse = 1;
      fprintf(tester, "if (isA(a_%s, %s))\n", className, typeName);
      fprintf(tester, "    {\n");
      fprintf(tester, "      if (log && !findString(\"%s\", %sSubs))\n",
	      typeName, className);
      fprintf(tester, "        {warnBlockBug(\"%s\", \"%s\");}\n",
	      typeName, className);
      subData = findClassData(typeName, classDatas);
      if ((subData->atts) || (subData->subs))
	{
	  fprintf(tester, "      else\n");
	  fprintf(tester, "\tcheck_%s(", typeName);
	  if ((classLen + typeLen + typeLen) > 36)
	    fprintf(tester, "\n\t  ");
	  fprintf(tester, "dynamic_cast<%s *>(a_%s), log);\n",
		  typeName, className);
	}
      fprintf(tester, "    }\n");
    }
  fprintf(tester, "}\n");
  printStarLine(tester);  
}

/********************************************************************/

/* printTestFunctions

Returned Value: none

Called By: printConfTester

This prints the tester functions for all the classes.

Four different subordinate functions may be called depending on
whether the class:
1. is a DMIS block
2. has attributes
3. has subclasses.

*/

void printTestFunctions(                /*  ARGUMENTS                */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * tester)                         /* file to print to          */
{
  int k;
  int n;
  classData * data;
  char * className;

  for (n = 0; n < 26; n++)
    {
      for (k=0; classDatas[n][k]; k++)
	{
	  data = classDatas[n][k];
	  className = data->name;
	  if ((data->isBlock) && (data->subs))
	    printTestBlockSubChecker(data->subs, className, classDatas,tester);
	  else if ((data->isBlock) && (data->atts))
	    printTestBlockAttChecker(data->atts, className, classDatas,tester);
	  else if (data->subs)
	    printTestSubChecker(data->subs, className, classDatas, tester);
	  else if (data->atts)
	    printTestAttChecker(data->atts, className, classDatas, tester);
	}
    }
}

/********************************************************************/

/* printTestStart

Returned Value: none

Called By: printConfTester

This prints at the beginning of the tester:
 - disclaimer
 - #include
 - statement counter declarations
 - tester function declarations

Tester function declarations are printed for all classes except those
that have neither subtypes nor attributes.

*/

void printTestStart(                    /*  ARGUMENTS                */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * tester)                         /* file to print to          */
{
  int k;
  int n;
  classData * data;
  stringCell * aCell;
  char * className;

  fprintf(tester,
"/************************************************************************\n"
"  DISCLAIMER:\n"
"  This software was produced by the National Institute of Standards\n"
"  and Technology (NIST), an agency of the U.S. government, and by statute\n"
"  is not subject to copyright in the United States.  Recipients of this\n"
"  software assume all responsibility associated with its operation,\n"
"  modification, maintenance, and subsequent redistribution.\n"
"\n"
"  See NIST Administration Manual 4.09.07 b and Appendix I.\n"
"************************************************************************/\n"
"\n"
"#include \"dmisConformanceTesterStart.cc\"\n"
"\n"
"namespace NDTU {\n"
"\n");
  for (aCell = &statementNames; aCell; aCell = aCell->next)
    {
      fprintf(tester, "int %ss;\n", aCell->data);
    }
  for (n = 0; n < 26; n++)
    {
      for (k=0; classDatas[n][k]; k++)
	{
	  data = classDatas[n][k];
	  className = data->name;
	  if ((data->subs) || (data->atts))
	    {
	      fprintf(tester, "void check_%s(", className);
	      if (strlen(className) < 18)
		{
		  fprintf(tester, "%s * a_%s, int log);\n",
			  className, className);
		}
	      else
		{
		  fprintf(tester, "\n     %s * a_%s, int log);\n",
			  className, className);
		}
	    }
	}
    }
}

/********************************************************************/

/* printTestSubChecker

Returned Value: none

Called By: printTestFunctions

This prints the checking function for a class that has subtypes.  The
function that is printed checks that the subtype of an instance of the
named class is allowed in the conformance class.  If the subtype is
not allowed the checking function prints an error message. If (1) the
subtype is allowed and (2) the subtype is a class that has either
subtypes or attributes, then the checking function for the subtype is
called.

Example: check_windefMinor is the function that is printed for the
windefMinor class. a_windefMinor is either a windefEdgeln or a windefBox.

void check_windefMinor(
 windefMinor * a_windefMinor,
 int log)
{
  if (isA(a_windefMinor, windefEdgeln))
    {
      adjustLevels(&windefMinorSubs_windefEdgeln, "windefEdgeln");
      if (log && !findString("windefEdgeln", windefMinorSubs))
	{
	  warnSub("windefEdgeln", "windefMinor", a_windefMinor);
	  check_windefEdgeln(
	    dynamic_cast<windefEdgeln *>(a_windefMinor), 0);
	}
      else
	check_windefEdgeln(
	  dynamic_cast<windefEdgeln *>(a_windefMinor), log);
    }
  else if (isA(a_windefMinor, windefBox))
    {
      adjustLevels(&windefMinorSubs_windefBox, "windefBox");
      if (log && !findString("windefBox", windefMinorSubs))
	{
	  warnSub("windefBox", "windefMinor", a_windefMinor);
	  check_windefBox(dynamic_cast<windefBox *>(a_windefMinor), 0);
	}
      else
	check_windefBox(dynamic_cast<windefBox *>(a_windefMinor), log);
    }
}

When this is called, strCell is the cell whose data is the first
subtype name in a list of subtype names.

This is using multiple "if...return" blocks rather than "if...else if"
blocks because the Visual C++ compiler can only have a limited number of
"if...else if" blocks (each of which causes a deeper nesting), and
dmisFreeStatement has more than the allowed number (which is somewhere
around 100).

*/

void printTestSubChecker(               /*  ARGUMENTS                */
 stringCell * strCell,                  /* cell of first type name   */
 char * className,                      /* name of class             */
 classData * classDatas[26][CLASSSIZE], /* array of class data lists */
 FILE * tester)                         /* file to print to          */
{
  char * typeName;
  classData * subData;
  int classLen;         // length of className
  int typeLen;          // length of typeName

  classLen = strlen(className);
  fprintf(tester, "void check_%s(\n", className);
  fprintf(tester, " %s * a_%s,\n", className, className);
  fprintf(tester, " int log)\n");
  fprintf(tester, "{\n");
  for (; strCell; strCell = strCell->next)
    {
      typeName = strCell->data;
      typeLen = strlen(typeName);
      fprintf(tester, "  if (isA(a_%s, %s))\n", className, typeName);
      fprintf(tester, "    {\n");
      fprintf(tester, "      adjustLevels(&%sSubs_%s,", className, typeName);
      if ((classLen + typeLen + typeLen) > 48)
	fprintf(tester, "\n\t\t  ");
      fprintf(tester, " \"%s\");\n", typeName);
      fprintf(tester, "      if (log && !findString(\"%s\",", typeName);
      if ((typeLen + classLen) > 40)
	fprintf(tester, "\n\t\t\t    ");
      fprintf(tester, " %sSubs))\n", className);
      fprintf(tester, "\t{\n");
      fprintf(tester, "\t  warnSub(\"%s\", \"%s\",", typeName, className);
      if ((classLen + classLen + typeLen) > 50)
	fprintf(tester, "\n\t\t ");
      fprintf(tester, " a_%s);\n", className);
      subData = findClassData(typeName, classDatas);
      if ((subData->atts) || (subData->subs))
	{
	  fprintf(tester, "\t  check_%s(", typeName);
	  if ((classLen + typeLen + typeLen) > 33)
	    fprintf(tester, "\n\t    ");
	  fprintf(tester, "dynamic_cast<%s *>(a_%s), 0);\n",
		  typeName, className);
	  fprintf(tester, "\t}\n");
	  fprintf(tester, "      else\n");
	  fprintf(tester, "\tcheck_%s(", typeName);
	  if ((classLen + typeLen + typeLen) > 33)
	    fprintf(tester, "\n\t  ");
	  fprintf(tester, "dynamic_cast<%s *>(a_%s), log);\n",
		  typeName, className);
	}
      else
	fprintf(tester, "\t}\n");
      fprintf(tester, "      return;\n");
      fprintf(tester, "    }\n");
    }
  fprintf(tester, "}\n");
  printStarLine(tester);
}

/********************************************************************/

/* printUpcase

Returned Value: none

Called By:
  printYaccAction
  printYaccIncDefs

This prints the given text in the given file, changing any
lower case letter to the corresponding upper case letter.

*/

void printUpcase( /* ARGUMENTS        */
 char * text,     /* text to print    */
 FILE * aFile)    /* file to print in */
{
  int m;
  char c;
  static int d = ('A' - 'a');

  for (m = 0; (c = text[m]); m++)
    {
      fputc((((c >= 'a') && (c <= 'z')) ? (c + d) : c), aFile);
    }
}

/********************************************************************/

/* printYacc

Returned Value: none

Called By: main

This prints the YACC file. If the file cannot be opened, this
prints an error message and exits.

*/

void printYacc(       /* ARGUMENTS                       */
 char * baseFileName) /* base name of YACC file to print */
{
  FILE * yaccFile;
  char fileName[NAMESIZE];

  sprintf(fileName, "%s.y", baseFileName);
  yaccFile = fopen(fileName, "w");
  if (yaccFile == 0)
    {
      fprintf(stderr, "Unable to open file %s for writing\n", fileName);
      exit(1);
    }
  printYaccStart(yaccFile, baseFileName);
  printYaccMiddle(yaccFile);
  printYaccProductions(yaccFile);
  fprintf(yaccFile, "%%%%\n");
  fclose(yaccFile);
}

/********************************************************************/

/* printYaccAction

Returned Value: none

Called By:
  printYaccForNewDefs
  printYaccForPlain

This prints in the code file the action to be taken when a YACC rule
is triggered. The rule and the action are both represented in the exps
list of expressions.

printYaccActionItem is called to print the arguments to the
constructor.  It prints 0 (for nullExp), true (for trueExp), false
(for falseExp) and $N for terminals and nonterminals, but ignores
everything else. To determine the value of N (so that N indicates the
correct position of the expression in the rule for which this action
is being printed), printYaccActionItem counts everything except
nullExp, trueExp, and falseExp.

For example, if the class name is "thing_2" and the prepared definition
for thing is:  ALLSA C EXCEPT C saLabel nullExp trueExp C AUTO
this will print :
   { $$ = new thing_2($5, 0, true); }

If className is macroBlock, this adds macroIsReal as the last argument
of the constructor. This is to help handle the situation where an
undefined macro was referenced and the preprocessor has inserted a
dummy macro to work around the error.

If the class name is labelNameAt (the name for an @variable label),
this prints a second action that sets aLabelType to zero.

If theType of the last expression in exps is ENDLINE and this is not a
noParseStm, this also prints an action that adds the production to the
dmisStms list. This assumes ENDLINE occurs only at the end of
statements for which we want to record the number of occurrences in a
DMIS file. In the debnf files for DMIS, ENDLINE occurs only at the end
of the definitions of statements, so this works. All statements in a
MACRO are noParseStms. When the MACRO is CALLed, the statements (with
the call arguments substituted for the MACRO arguments) get added to
the dmisStms list.

*/

void printYaccAction( /* ARGUMENTS                                   */
 char * prodName,     /* name of production for action               */
 char * className,    /* name of class to instantiate                */
 expList * exps,      /* expressions to use in making class instance */
 FILE * yaccFile)     /* YACC file to print in                       */
{
  expListCell * expCell;
  int n;            // counter for exps
  int commaFlag;    // 1 means a comma and space are needed, 0 = not
  
  n = 1;
  commaFlag = 0;
  fprintf(yaccFile, "\t    { $$ = new %s(", className);
  for (expCell = exps->first ; expCell; expCell = expCell->next)
    {
      printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
    }
  if (strcmp(className, "macroBlock") == 0)
    {
      fprintf(yaccFile, ", macroIsReal);\n");
      fprintf(yaccFile, "\t      macroIsReal = 1; }\n");
    }
  else if (strcmp(className, "labelNameAt") == 0)
    {
      fprintf(yaccFile, ");\n");
      fprintf(yaccFile, "\t      aLabelType = 0; }\n");
    }
  else if (exps->last && (exps->last->data->theType == ENDLINE) &&
	   strcmp(prodName, "noParseStm"))
    {
      fprintf(yaccFile, ");\n");
      fprintf(yaccFile, "\t      dmisStms.push_back($$);\n");
      fprintf(yaccFile, "\t    }\n");
    }
  else if ((strcmp(prodName, "geoalgExternFunc") == 0) &&
	   (expCell = exps->first) &&
	   (expCell->data->theType == KEYWORD) &&
	   (strcmp(expCell->data->itemName, "DMIS") == 0) &&
	   (expCell = expCell->next) &&
	   (expCell = expCell->next) &&
	   (expCell->data->theType == NONTERMINAL) &&
	   (strcmp(expCell->data->itemName, "mLabel") == 0))
    { // don't signal an error if EXTERN macro called
      fprintf(yaccFile, ");\n");
      fprintf(yaccFile, "\t      aLabelFound = 1; }\n");
    }
  else
    fprintf(yaccFile, "); }\n");
}

/********************************************************************/

/* printYaccActionItem

Returned Value: none

Called By:
  printYaccAction
  printYaccFirstAction
  printYaccForFixDef1
  printYaccForFixDef2
  printYaccForFixList1

This prints one argument to a call to a class constructor. The
calling functions call it multiple times to print all the arguments.

This prints 0 (for nullExp), true (for trueExp), false (for
falseExp) and $N for terminals and nonterminals, but ignores
everything else. To determine the value of N (so that N indicates the
correct position of the expression in the rule for which this action
is being printed), this counts everything except nullExp, trueExp, and
falseExp.

See example in documentation of printYaccAction.

Commas must be printed between the arguments of a constructor.  The
caller will not know whether or not this function has printed an item,
so the commaFlag is used here and in the caller. The caller sets the
commaFlag to 0 before its first call to this function, and when this
function prints an item, this function resets the commaFlag to 1.

*/

void printYaccActionItem( /* ARGUMENTS                            */
 expression * exp,        /* expression to print from             */
 int * commaFlag,         /* 1 = print comma, 0=don't, reset here */
 int * n,                 /* counter for arguments of rule        */
 FILE * yaccFile)         /* YACC file to print in                */
{
  if (exp == nullExp)
    {
      if (*commaFlag)
	fprintf(yaccFile, ", ");
      *commaFlag = 1;
      fprintf(yaccFile, "0");
    }
  else if (exp == trueExp)
    {
      if (*commaFlag)
	fprintf(yaccFile, ", ");
      *commaFlag = 1;
      fprintf(yaccFile, "true");
    }
  else if (exp == falseExp)
    {
      if (*commaFlag) // printed arg, so print comma and newline
	fprintf(yaccFile, ", ");
      *commaFlag = 1;
      fprintf(yaccFile, "false");
    }
  else if (exp->theType == TERMINAL)
    {
      if (*commaFlag) // printed arg, so print comma and newline
	fprintf(yaccFile, ", ");
      *commaFlag = 1;
      fprintf(yaccFile, "$%d", (*n)++);
    }
  else if (exp->theType == NONTERMINAL)
    {
      if (*commaFlag) // printed arg, so print comma and newline
	fprintf(yaccFile, ", ");
      *commaFlag = 1;
      fprintf(yaccFile, "$%d", (*n)++);
    }
  else
    (*n)++;
}

/********************************************************************/

/* printYaccDatMinor

Returned Value: none

Called By: printYaccProduction

This is called to handle DATDEF and DATTRG. These commands put
the label to be defined at the end, following a comma. We want
to check that there are no undefined labels up to the comma.
We also want to check that the DAT or DATTRG label being defined
was not defined previously.

To make the rule, first this prints all the expressions up to the
expression whose index is commaIndex. That expression is assumed to be
a comma.  This prints "undefCheckComma" instead of the comma.  Then
this prints the label. When this is called, it is known that there are
commaIndex+2 expressions, so there is nothing further to print in
the rule after that.

This then prints the action for the rule. The first part of the action
is to check that the label has not yet been defined, and to record the
label name in the list for the appropriate label type, as given by the
datName.  The action also sets aLabelFound back to 1. If the label is
not doubly defined, aLabelFound will have been set to 0 when the label
was read. The end of the action sets $$. For setting $$, it is
assumed here that the second definition of datdefMinor might be
commented out, but the first definition will not be. That is checked
in printYaccProduction before this function is called.

*/

void printYaccDatMinor( /* ARGUMENTS                                */
 definition * def,      /* definition to print                      */
 char orOrSpace,        /* '|' or ' '                               */
 int commaIndex,        /* index of comma to replace, always 1 or 3 */
 const char * datName,  /* "DAT" or "DATTRG"                        */
 FILE * yaccFile)       /* YACC file to print in                    */
{
  int n;
  expListCell * expCell;

  fprintf(yaccFile, "\t%c ", orOrSpace);
  for (n = 0, expCell = def->expressions->first;
       expCell;
       n++, expCell = expCell->next)
    {
      if (n == commaIndex)
	fprintf(yaccFile, " undefCheckComma");
      else
	printYaccExpression(expCell->data, yaccFile);
    }
  fprintf(yaccFile, "\n");
  fprintf(yaccFile,
"          {\n"
"            if (aLabelFound == 1)\n"
"              {\n"
"                sprintf(warningMessage,\n"
"			\"label doubly defined?: %%s\", aLabel);\n"
"                warn(warningMessage);\n"
"              }\n"
"            else\n"
"              {\n"
"                labels[%s].record(aLabel);\n"
"                aLabelFound = 1;\n", datName);
  if (commaIndex == 1)
    {
      fprintf(yaccFile, "\t        $$ = new %s($1, $3);\n", def->className);
    }
  else
    { // must be working on a datdefMinor_dattrgList
      fprintf(yaccFile, 
	      "\t        $$ = new datdefMinor_dattrgList($1, $3, $5);\n");
    }
  fprintf(yaccFile,  
"              }\n"
"          }\n");
}

/********************************************************************/

/* printYaccDoCall

Returned Value: none

Called By: printYaccStart

This prints the doCall function preceded by a lot of documentation.
Read the documentation to understand how CALL is handled.

*/

void printYaccDoCall( /* ARGUMENTS             */
 FILE * yaccFile)     /* YACC file to print in */
{
  fprintf(yaccFile,
"/* doCall\n"
"\n"
"This returns 1 if the line being read is a CALL line. Otherwise, it\n"
"returns 0.\n"
"\n"
"If a call starts with CALL/M(label), then callType is 1. In this case\n"
"a MACRO is being called that should already have been defined in the\n"
"file being read, so a check is made of whether the CALLed MACRO (whose\n"
"name has been placed in macroName) has been defined. If not, an error\n"
"message is recorded.\n"
"\n"
"If a call starts with CALL/EXTERN,DMIS,M(label), then callType is 2.\n"
"In this case, a MACRO is being called that is probably not defined in\n"
"the file being read. If the same macro was called previously, however,\n"
"a dummy macro by that name has been defined, so a check is made of\n"
"whether the CALLed MACRO (whose name has been placed in macroName) has\n"
"been defined.\n"
"\n"
"If a call starts with CALL/ followed by anything else, then callType\n"
"is 3. In this case, only the CALL line and a following ENDMAC line\n"
"are printed.\n"
"\n"
"If callType is 1 or 2:\n"
"\n"
"   If the called macro has not yet been defined, then a dummy macro is\n"
"   is put into the preprocessed file just before the CALL. The dummy MACRO\n"
"   has the name given in the CALL and has the same number of arguments as\n"
"   the call. Having the same number of arguments is necessary in case\n"
"   another call is made to the same macro, since then the number of\n"
"   arguments will be checked. The dummy MACRO has only two lines (a MACRO\n"
"   line and an ENDMAC line). Inserting a dummy MACRO will prevent the\n"
"   parser from reporting an undefined label when it reads the M(label)\n"
"   in the CALL.\n"
"\n"
"   The CALL line is printed.\n"
"\n"
"   The modified text of the CALLed macro (without the MACRO line but\n"
"   with the ENDMAC line) is inserted after the CALL statement. If\n"
"   the CALLed macro is a dummy, this consists of only an ENDMAC line.\n"
"   The text of the macro is modified by replacing appearances of macro\n"
"   arguments with the corresponding arguments from the CALL.\n"
"\n"
"When the parser parses a CALL in the preprocessed file, it will then\n"
"be parsing the macro after the CALL substitutions are made (so there\n"
"is no need to deal with untyped macro arguments). Thus, the parser will\n"
"deal gracefully with all macros, including undefined EXTERN macros.\n"
"At the point in the file where a macro is defined, the macro is parsed\n"
"as though it is unintelligible text (so there is no need to deal\n"
"with untyped macro arguments).\n"
"\n"
"The parser considers that a CALL block starts with CALL and ends with\n"
"ENDMAC. The EBNF has been modified to define a CALL block that way.\n"
"The purpose of defining a CALL block is to establish the scope of any\n"
"variables defined in a CALLed MACRO.\n"
"\n"
"This is all part of the \"text substitution\" approach to implementing\n"
"DMIS macros, which is like the C and C++ approach and satisfies the\n"
"requirements on macros given in the DMIS spec.\n"
"\n"
"*/\n"
"\n"
"int doCall(       /* ARGUMENTS                          */\n"
" char * buffer,   /* buffer containing a DMIS statement */\n"
" int lineNumber,  /* number of line starting statement  */\n"
" FILE * out)      /* file to write preprocessed DMIS to */\n"
"{\n"
"  static char macroName[256];   // name of CALLed macro\n"
"  macroRep * theMacro;          // the CALLed macro\n"
"  static char text[1024];       // text of a line of dummy macro\n"
"  int m;                        // counter for characters of text\n"
"  char argName[20];             // name of an argument to dummy macro\n"
"  stringList * macroArgs;       // arguments of dummy macro\n"
"  stringList * macroText;       // text of dummy macro\n"
"  static int callType;          // 0=not, 1=M, 2=EXTERN,DMIS,M, 3=other EXTERN\n"
"  static stringList * callArgs; // arguments of call\n"
"  stringListCell * argCell;     // cell of callArgs\n"
"  int numberArgs;               // the number of arguments to the CALL\n"
"  int n;                        // counter for arguments\n"
"\n"
"  callType = isCall(buffer, macroName);\n"
"  if ((callType == 1) || (callType == 2))\n"
"    {\n"
"      callArgs = new stringList;\n"
"      if (!findCallArgs(buffer, callArgs, &numberArgs))\n"
"	{ // quit if arguments to the call are unreadable\n"
"	  fprintf(stderr, \"%%d: bad call arguments. Quitting.\\n\",\n"
"		  lineNumber);\n"
"	  exit(1);\n"
"	}\n"
"      if ((theMacro = findMacro(macroName)) == 0)\n"
"	{ // macro not yet defined in file, so print and store dummy macro\n"
"	  if (callType == 1)\n"
"	    { // macro was expected to be defined but was not found\n"
"	      printf(\"%%d: Error - undefined MACRO called.\\n\\n\",\n"
"		      lineNumber);\n"
"	      numErrors++;\n"
"	    }\n"
"	  macroArgs = new stringList;\n"
"	  macroText = new stringList;\n"
"	  m = sprintf(text, \"M(%%s)=MACRO\", macroName);\n"
"	  if (numberArgs > 0)\n"
"	    {\n"
"	      m = (m + sprintf((text+m), \"/\"));\n"
"	      for (argCell = callArgs->first, n = 0;\n"
"		   argCell;\n"
"		   argCell = argCell->next, n++)\n"
"		{\n"
"		  sprintf(argName, \"arg%%d\", n);\n"
"		  m = (m + sprintf((text+m), \"%%s\", argName));\n"
"		  if (argCell->next)\n"
"		    m = (m + sprintf((text+m), \",\"));\n"
"		  macroArgs->pushBack(strdup(argName));\n"
"		}\n"
"	      \n"
"	    }\n"
"	  sprintf((text+m), \"%%c%%c\", 13, 10);\n"
"	  macroText->pushBack(strdup(text));\n"
"	  fprintf(out, \"%%d|$$Next macro is a dummy - qwerty%%c%%c\",\n"
"	          lineNumber, 13, 10);\n"
"	  fprintf(out, \"%%d|%%s\", lineNumber, text);	  \n"
"	  sprintf(text, \"ENDMAC%%c%%c\", 13, 10);\n"
"	  macroText->pushBack(strdup(text));\n"
"	  fprintf(out, \"%%d|%%s\", lineNumber, text);\n"
"	  theMacro = new macroRep(macroText, macroArgs, macroName);\n"
"	  macros.pushBack(theMacro);\n"
"	}\n"
"      fprintf(out, \"%%d|%%s\", lineNumber, buffer);\n"
"      insertCalledMacro(callArgs, theMacro, numberArgs, lineNumber, out);\n"
"      return 1;\n"
"    }\n"
"  else if (callType == 3)\n"
"    {\n"
"      fprintf(out, \"%%d|%%s\", lineNumber, buffer);\n"
"      fprintf(out, \"%%d|ENDMAC%%c%%c\", lineNumber, 13, 10);\n"
"      return 1;\n"
"    }\n"
"  return 0;\n"
"}\n");
}

/********************************************************************/

/* printYaccDoLabel

Returned Value: none

Called By: printYaccStart

This prints in the YACC file the doLabel function, preceded by some
documentation.

*/

void printYaccDoLabel( /* ARGUMENTS             */
 FILE * yaccFile)      /* YACC file to print in */
{
  fprintf(yaccFile,
"/* doLabel\n"
"\n"
"This is called by the actions for productions that define labels. The\n"
"general idea is that when a label has been read, we need to determine\n"
"if the label has already been defined. In some cases it is an error\n"
"if the label is not defined. In other cases it is an error if the\n"
"label is defined. The type of the label is in the labelType argument,\n"
"and the name of the label is in the labelName argument.\n"
"\n"
"If a label error has already occurred while reading a DMIS statement,\n"
"that is indicated by aLabelFound being 0. In this case we want to\n"
"remember the type and name of the label that caused the error so it\n"
"can be reported after reading the statement is finished.\n"
"\n"
"If the global variable aLabelFound (0 or 1) has been set and the\n"
"labelName argument is not NULL, then the global variable aLabelType is\n"
"set to the labelType argument and the global variable aLabel is set to\n"
"the labelName argument.  Then aLabelFound is reset to to indicate\n"
"whether a label of the given labelType and labelName has already been\n"
"recorded.\n"
"\n"
"*/\n"
"\n"
"void doLabel(\n"
"  int labelType,\n"
"  char * labelName)\n"
"{\n"
"  if (aLabelFound && labelName)\n"
"    {\n"
"      aLabelType = labelType;\n"
"      aLabel = labelName;\n"
"      aLabelFound = labels[labelType].find(labelName);\n"
"    }\n"
"}\n");
}

/********************************************************************/

/* printYaccDoMacro

Returned Value: none

Called By: printYaccStart

This prints the doMacro function preceded by a lot of documentation.
Read the documentation to understand how MACRO is handled.

*/

void printYaccDoMacro( /* ARGUMENTS             */
 FILE * yaccFile)      /* YACC file to print in */
{
  fprintf(yaccFile,
"/* doMacro\n"
"\n"
"A. If buffer is 0, just reset the static inMacro local variable to 0.\n"
"The inMacro variable should be zero when parsing a DMIS input file\n"
"starts. If a number of files are being parsed and there\n"
"is an error in a macro in a file, inMacro may have a non-zero\n"
"value when parsing that file is finished. To do the resetting,\n"
"resetParser calls doMacro(0,0) before parsing a new file.\n"
"\n"
"B. Otherwise, if inMacro is 0, meaning we are not already inside a\n"
"MACRO, this reads the buffer and checks whether a macro is starting.\n"
"If so, it does B1 and B2. Otherwise, it does nothing more.\n"
"\n"
"B1. A new macroRep is constructed for the macro. If no macro by the\n"
"same name is already recorded, the new macro is recorded (the parser\n"
"will signal an error later if a macro by the same name is already\n"
"recorded).\n"
"\n"
"B2. Next, this (1) tries to read the arguments of the macro from the\n"
"macroLine and store them in the macroArgs [if reading arguments fails,\n"
"this prints an error message and quits], and (2) sets inMacro to 1.\n"
"\n"
"C. Otherwise, (inMacro must be 1) we are on a line of a macro after\n"
"the the first line. This does C1, C2, or C3.\n"
"\n"
"C1. If the statement in the buffer defines a MACRO (so that one macro\n"
"definition is inside another), this prints an error message and quits.\n"
"\n"
"C2. If the statement in the buffer defines a CALL, this prints an\n"
"error message and quits.\n"
"\n"
"C3. This adds the macroLine to the macroText, and if this is an ENDMAC\n"
"line, this sets inMacro to 0.\n"
"\n"
"\n"
"Note: The macroText does not include line numbers.\n"
"\n"
"*/\n"
"\n"
"void doMacro(\n"
" char * buffer,\n"
" int lineNumber)\n"
"{\n"
"  macroRep * aMacro;              // macro to deal with\n"
"  static char callName[256];      // name of macro in CALL\n"
"  static char * macroName;        // name of macro in MACRO\n"
"  static stringList * macroText;  // text of macro\n"
"  static stringList * macroArgs;  // arguments of macro\n"
"  static int inMacro = 0;         // 0=not in macro, 1=in macro body\n"
"\n"
"  if (buffer == 0)\n"
"    { // for resetting inMacro, call doMacro with buffer = 0\n"
"      inMacro = 0;\n"
"    }\n"
"  else if (inMacro == 0)\n"
"    {\n"
"      if (isMacro(buffer, &macroName))\n"
"	{\n"
"	  macroText = new stringList;\n"
"	  macroArgs = new stringList;\n"
"	  aMacro = findMacro(macroName);\n"
"	  if (!aMacro)\n"
"	    {\n"
"	      aMacro = new macroRep(macroText, macroArgs, macroName);\n"
"	      macros.pushBack(aMacro);\n"
"	    }\n"
"	  if (!findMacroArgs(buffer, macroArgs))\n"
"	    {\n"
"	      fprintf(stderr, \"%%d: bad macro arguments. Quitting.\\n\",\n"
"		      lineNumber);\n"
"	      exit(1);\n"
"	    }\n"
"	  macroText->pushBack(strdup(buffer));\n"
"	  inMacro = 1;\n"
"	}\n"
"    } // below here, (inMacro == 1)\n"
"  else if (isMacro(buffer, &macroName))\n"
"    {\n"
"      fprintf(stderr, \"%%d: macro defined inside macro. Quitting.\\n\",\n"
"	      lineNumber);\n"
"      exit(1);\n"
"    }\n"
"  else if (isCall(buffer, callName))\n"
"    {\n"
"      fprintf(stderr, \"%%d: cannot handle call inside macro. Quitting.\\n\",\n"
"	      lineNumber);\n"
"      exit(1);\n"
"    }\n"
"  else\n"
"    { // inside a macro\n"
"      macroText->pushBack(strdup(buffer));\n"
"      if (isEndmac(buffer))\n"
"	inMacro = 0;\n"
"    }\n"
"}\n");
}

/********************************************************************/

/* printYaccExpression

Returned Value: none

Called By:
  printYaccDatMinor
  printYaccRule

This prints one expression into the YACC file, preceded by a space.

When printYaccExpression is called, no optionals should remain.

The POWER token (which is '**') is recognized in lex as the type
TERMINALSTRING. Here, if the type returned by lex is TERMINALSTRING,
and the text is "**", POWER is printed into the YACC file.

*/

void printYaccExpression( /* ARGUMENTS             */
 expression * exp,        /* expression to print   */
 FILE * yaccFile)         /* YACC file to print in */
{
  if (exp == commaExp)
    fprintf(yaccFile, " C");
  else if ((exp->theType == KEYWORD)     ||
	   (exp->theType == NONTERMINAL) ||
	   (exp->theType == TERMINAL))
    fprintf(yaccFile, " %s", exp->itemName);
  else if (exp->theType == ENDLINE)
    fprintf(yaccFile, " endOfLine");
  else if (exp->theType == ONECHAR)
    {
      switch (exp->itemName[0])
	{
	case ',':
	  fprintf(yaccFile, " C");
	  break;
	case '+':
	  fprintf(yaccFile, " PLUS");
	  break;
	case '-':
	  fprintf(yaccFile, " MINUS");
	  break;
	case '*':
	  fprintf(yaccFile, " TIMES");
	  break;
	case '/':
	  fprintf(yaccFile, " SLASH");
	  break;
	case '=':
	  fprintf(yaccFile, " EQUALS");
	  break;
	case ':':
	  fprintf(yaccFile, " COLON");
	  break;
	case '@':
	  fprintf(yaccFile, " AT");
	  break;
	case '[':
	  fprintf(yaccFile, " LBOX");
	  break;
	case ']':
	  fprintf(yaccFile, " RBOX");
	  break;
	case '(':
	  fprintf(yaccFile, " LPAREN");
	  break;
	case ')':
	  fprintf(yaccFile, " RPAREN");
	  break;
	default:
	  fprintf(stderr, "Unknown onechar type in printYaccExpression\n");
	  exit(1);
	} 
    }
  else if (exp->theType == TERMINALSTRING)
    {
      if (strcmp("**", exp->itemName) == 0)
	{
	  fprintf(yaccFile, " POWER");
	}
      else
	{
	  fprintf(stderr, "Bad terminal string in printYaccExpression\n");
	  exit(1);
	}
    }
  else if ((exp->theType == OPTIONAL) || (exp->theType == TWOCHAR))
    {
      fprintf(stderr, "Bad expression type in printYaccExpression\n");
      exit(1);
    }
  else
    {
      fprintf(stderr, "Unknown expression type in printYaccExpression\n"); 
      exit(1);
    }
}

/********************************************************************/

/* printYaccFindCallArgs

Returned Value: none

Called By: printYaccStart

This prints the findCallArgs function preceded by some documentation.

*/

void printYaccFindCallArgs( /* ARGUMENTS             */
 FILE * yaccFile)           /* YACC file to print in */
{
  fprintf(yaccFile,
"/* findCallArgs\n"
"\n"
"This returns 1 if it is able to read and store all the call arguments\n"
"given in the text. Otherwise, it returns 0.\n"
"\n"
"This is called only if text is known to start with \"CALL/M(label)\" or\n"
"\"CALL/EXTERN,DMIS,M(label)\", so this skips to the first right\n"
"parenthesis. This should be followed by a comma and then any\n"
"arguments.\n"
"\n"
"Arguments enclosed in parentheses are expected to be label names.\n"
"\n"
"This sets numberArgs to the number of arguments found.\n"
"\n"
"*/\n"
"\n"
"int findCallArgs(\n"
" char * text,\n"
" stringList * callArgs,\n"
" int * numberArgs)\n"
"{\n"
"  int n;\n"
"  int k;\n"
"  char c;\n"
"  static char buffer[256];\n"
"\n"
"  *numberArgs = 0;\n"
"  for (n = 0; ((c = text[n]) != ')'); n++); //skip to right parenthesis\n"
"  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c == 13) // found call with no arguments\n"
"    {\n"
"      return 1;\n"
"    }\n"
"  else if (c != ',')\n"
"    return 0;\n"
"  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  for (; ((c = text[n]) != 13) ; )\n"
"    {\n"
"      if (c == '(')\n"
"	{ // copy parenthesized argument (for label) without the parentheses\n"
"	  for (k = 0, n++; (((c = text[n]) != 13) && (c != ')')); n++)\n"
"	    buffer[k++] = c;\n"
"	  if (c == ')')\n"
"	    n++;\n"
"	  else\n"
"	    return 0;\n"
"	}\n"
"      else if (c == '\\'')\n"
"	{ // copy quoted string with the quotes\n"
"	  buffer[0] = c;\n"
"	  for (k = 1, n++; (((c = text[n]) != 13) && (c != '\\'')); n++)\n"
"	    buffer[k++] = c;\n"
"	  if (c == '\\'')\n"
"	    {\n"
"	      buffer[k++] = c;\n"
"	      n++;\n"
"	    }\n"
"	  else\n"
"	    return 0;\n"
"	}\n"
"      else // not parenthesized or quoted\n"
"	{ // copy any other characters that are not comma, space, or endline.\n"
"	  for (k = 0;\n"
"	       (((c = text[n]) != 13) && (c != ' ') &&\n"
"		(c != '\\t') && (c != ','));\n"
"	       n++)\n"
"	    buffer[k++] = c;\n"
"	}\n"
"      buffer[k] = 0;\n"
"      callArgs->pushBack(strdup(buffer));\n"
"      (*numberArgs)++;\n"
"      for (; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"      if (c == ',')\n"
"	{\n"
"	  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"	}\n"
"      else if (c != 13)\n"
"	return 0;\n"
"    }\n"
"  return 1;\n"
"}\n");
}

/********************************************************************/

/* printYaccFindMacro

Returned Value: none

Called By: printYaccStart

This prints the findMacro function.

*/

void printYaccFindMacro( /* ARGUMENTS             */
 FILE * yaccFile)        /* YACC file to print in */
{
  fprintf(yaccFile,
"macroRep * findMacro(\n"
" char * name)\n"
"{\n"
"  macroListCell * macroCell;\n"
"\n"
"  for (macroCell = macros.first; macroCell; macroCell = macroCell->next)\n"
"    {\n"
"      if (strcmp(name, macroCell->theMacro->name) == 0)\n"
"	return macroCell->theMacro;\n"
"    }\n"
"  return 0;\n"
"}\n");
}

/********************************************************************/

/* printYaccFindMacroArgs

Returned Value: none

Called By: printYaccStart

This prints the findMacroArgs function preceded by some documentation.

*/

void printYaccFindMacroArgs( /* ARGUMENTS             */
 FILE * yaccFile)            /* YACC file to print in */
{
  fprintf(yaccFile,
"/* findMacroArgs\n"
"\n"
"This returns 1 if it is able to read and store all the macro arguments\n"
"given in the text. Otherwise, it returns 0.\n"
"\n"
"The text is known to start with \"M(label)=MACRO\", so this skips to the\n"
"equal sign and from there to the O at the end of MACRO.\n"
"\n"
"Lower case letters in the names of the macro arguments are changed\n"
"to upper case.\n"
"\n"
"Arguments are required to have the format of variable names. That is,\n"
"they must start with a letter and contain only letters, digits, and\n"
"underscores. Quoted variable names (which represent labels) are stored\n"
"without the quotes.\n"
"\n"
"*/\n"
"\n"
"int findMacroArgs(\n"
" char * text,\n"
" stringList * arguments)\n"
"{\n"
"  int n;\n"
"  int k;\n"
"  char c;\n"
"  static char buffer[256];\n"
"\n"
"  for (n = 0; text[n] != '='; n++); // find equal sign\n"
"  for (n++; (((c = text[n]) != 'o') && (c != 'O')); n++); //find o in MACRO\n"
"  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++); // skip space\n"
"  if (c == 13) // found macro with no arguments\n"
"    {\n"
"      return 1;\n"
"    }\n"
"  else if (c != '/')\n"
"    return 0;\n"
"  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++); // skip space\n"
"  for (; ((c = text[n]) != 13) ; )\n"
"    {\n"
"      if (c == '\\'')\n"
"	{ // copy quoted argument (for label) without the quotes\n"
"	  c = text[++n];\n"
"	  if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))))\n"
"	    return 0;\n"
"	  for (k = 0;\n"
"	       (((c = text[n]) == '_') || ((c >= '0') && (c <= '9')) ||\n"
"		((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));\n"
"	       n++)\n"
"	    buffer[k++] = (((c >= 'a') && (c <= 'z'))?(c + ('A' - 'a')):c);\n"
"	  if (c == '\\'')\n"
"	    n++;\n"
"	  else\n"
"	    return 0;\n"
"	}\n"
"      else if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))\n"
"	{ // copy argument starting with a letter\n"
"	  for (k = 0;\n"
"	       (((c = text[n]) == '_') || ((c >= '0') && (c <= '9')) ||\n"
"		((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));\n"
"	       n++)\n"
"	    buffer[k++] = (((c >= 'a') && (c <= 'z'))?(c + ('A' - 'a')):c);\n"
"	}\n"
"      else\n"
"	return 0;\n"
"      buffer[k] = 0;\n"
"      arguments->pushBack(strdup(buffer));\n"
"      for (; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"      if (c == ',')\n"
"	{\n"
"	  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"	}\n"
"      else if (c != 13)\n"
"	return 0;\n"
"    }\n"
"  return 1;\n"
"}\n");
}

/********************************************************************/

/* printYaccFirstAction

Returned Value: none

Called By:
  printYaccFirstProduction

This is similar to printYaccAction, except that it writes a second
line of each action, which is "tree = $$;". The tree variable is
global, and the name "tree" is hard-coded.

*/

void printYaccFirstAction( /* ARGUMENTS                                   */
 char * className,         /* name of class to instantiate                */
 expList * exps,           /* expressions to use in making class instance */
 FILE * yaccFile)          /* YACC file to print in                       */
{
  expListCell * expCell;
  int n;            // counter for exps
  int commaFlag;    // 1 means a comma and space are needed, 0 = not
  
  n = 1;
  commaFlag = 0;
  fprintf(yaccFile, "\t    { $$ = new %s(", className);
  for (expCell = exps->first ; expCell; expCell = expCell->next)
    {
      printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
    }
  fprintf(yaccFile, ");\n");
  fprintf(yaccFile, "\t      tree = $$;\n");
  fprintf(yaccFile, "\t    }\n");
}

/********************************************************************/

/* printYaccFirstProduction

Returned Value: none

Called By: printYaccProductions

This writes the rules and actions for the definitions of the first
production.  This writes a second line of each action, which is "tree
= $$;". The tree variable is global, and the name "tree" is
hard-coded.

*/

void printYaccFirstProduction( /* ARGUMENTS                 */
 production * prod,            /* first production to print */
 FILE * yaccFile)              /* YACC file to print in     */
{
  defListCell * defCell;
  defListCell * newDefCell;
  defList * newDefins;
  expList * exps;

  if ((prod->fixType != production::fixNone) ||
      (prod->isList) ||
      (prod->isSupertype == true))
    {
      fprintf(stderr,
	      "First production must be plain in printYaccFirstProduction\n");
      exit(1);
    }
  fprintf(yaccFile, "%s :\n", prod->lhs);

  makeNewDefs(prod->defs);
  for (defCell = prod->defs->first; defCell; defCell = defCell->next)
    {
      newDefins = defCell->data->newDefs;
      prepareDefsForPlain(newDefins);
      newDefCell = newDefins->first;
      for ( ; newDefCell; newDefCell = newDefCell->next)
	{
	  exps = newDefCell->data->expressions;
	  printYaccRule(exps,
			((defCell->back || newDefCell->back) ? '|' : ' '),
			yaccFile);
	  printYaccFirstAction(newDefCell->data->className, exps, yaccFile);
	}
    }
  fprintf(yaccFile, "\t;\n\n");
}

/********************************************************************/

/* printYaccForFixDef1

Returned Value: none

Called By: printYaccForFixList2

This expands a single definition from the newDefs of a production
into three definitions and then prints the rules and actions for
those three definitions.

Call the definition to be printed D.
Call the last expression in D END
Call the next-to-last expression in D NEXTOEND.

When this is called, it is known that:
1. END is an optional.
2. END has two expressions,
3. The itemName of the last of the two expressions in END is the name
   of the list being printed.
4. NEXTOEND has a prodValue that is a list; call it L.
5. One or more of the definitions of the list item for L ends
   in an optional.
6. END is always followed by an ENDLINE.

The production represented by NEXTOEND will have had a comma added to the
end of all of its definitions by insertItemAndCommaInList. Thus,
NEXTOEND must not appear at the end of a definition, so the definition
that would normally have NEXTOEND at the end is broken into two
definitions.

Example: Suppose defCell points to the following definition of
snslctWristList and the className in the definition is snslctWristItem_1.
In this example, END is [c, snslctWristList] and NEXTOEND is 
snslctWristAngleList.

    swLabel, c, snslctWristAngleList, [c, snslctWristList]

This will expand the definition first into two definitions as follows.

    swLabel, c, snslctWristAngleList nullExp
  | swLabel, c, snslctWristAngleList, c, snslctWristList

Then it will:
1. make a copy of the first definition,
2. add the copy to the front of the list of definitions,
3. delete the nullExp from the end of the (new) first definition
4. delete snslctWristAngleList from the end of the (new) first definition,
5. add snslctWristAngleItem at the end of the first and second definition
   to get:

    swLabel, c, snslctWristAngleItem
  | swLabel, c, snslctWristAngleList, snslctWristAngleItem
  | swLabel, c, snslctWristAngleList, c, snslctWristList


Then this will print:

	  swLabel C snslctWristAngleItem
            { $$ = new std::list<snslctWristItem *>;
	      $$->push_front(new snslctWristItem_1
		  ($1, new std::list<snslctWristAngleItem *>(1, $3))); }
	| swLabel C snslctWristAngleList snslctWristAngleItem
	    { $$ = new std::list<snslctWristItem *>;
	      $3->push_back($4);
	      $$->push_front(new snslctWristItem_1($1, $3)); }
	| swLabel C snslctWristAngleList snslctWristList
	    { $$ = $4;
	      $$->push_front(new snslctWristItem_1($1, $3)); }

This does the following:
1. Make newDefs for the definition pointed to by defCell.
2. Check that the length of the newDefs is 2 (implying there
   were no optionals in the definition except at the end.)
3. Check that the expression at the end of the second of the newDefs
   represents the recursion of the list being printed.
4. Make a second copy of the first of the newDefs and insert it
   at the front of the newDefs list.
5. Remove the last expression from the first of the newDefs.
6. Insert the list item 

*/

void printYaccForFixDef1( /* ARGUMENTS                                 */
 production * prod,       /* production being printed (a list)         */
 defListCell * defCell,   /* cell pointing to definition being printed */
 char * parentName,       /* parent class name                         */
 char orOrSpace,          /* '|' or ' '                                */
 FILE * yaccFile)         /* YACC file to print in                     */
{
  defListCell * newDefCell;
  defList * newDefins;
  expList * exps;
  expListCell * expCell;
  expression * exp;
  int commaFlag;  // 1=a comma and space are needed, 0=not
  int n;
  production * listItem;
  production * nestedListItem;

  listItem = prod->defs->first->data->expressions->last->data->prodValue;
  newDefins = new defList(defCell->data->dup());
  defCell->data->newDefs = newDefins;
  prepareDefsForPlain(newDefins);
  if (newDefins->findLength() != 2)
    {
      fprintf(stderr, "Cannot handle optionals in printYaccForFixDef1\n");
      exit(1);
    }
  exp = newDefins->last->data->expressions->last->data;
  if ((exp->theType != NONTERMINAL) || strcmp(prod->lhs, exp->itemName))
    {
      fprintf(stderr, "Bug in printYaccForFixDef1\n");
      exit(1);
    }
  newDefins->pushFront(newDefins->first->data->dup());

  // modify first expression and print rule and action for it
  newDefCell = newDefins->first;
  exps = newDefCell->data->expressions;
  nestedListItem = exps->last->back->data->prodValue->defs->
    first->data->expressions->last->data->prodValue;
  exps->removeCell(exps->last);
  exps->removeCell(exps->last);
  exps->pushBack(new expression(NONTERMINAL, strdup(nestedListItem->lhs),
				0, 0, nestedListItem));
  printYaccRule(exps, orOrSpace, yaccFile);
  orOrSpace = '|';
  commaFlag = 0;
  n = 1;
  fprintf(yaccFile, "            { $$ = new std::list<%s *>;\n",
	  listItem->lhs);
  fprintf(yaccFile, "              $$->push_front(new %s\n",
	  defCell->data->className);
  fprintf(yaccFile, "                    (");
  for (expCell = exps->first; expCell->next; expCell = expCell->next)
    { // leave off last expression, which is nullExp
      printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
    }
  fprintf(yaccFile, ", new std::list<%s *>(1, $%d))); }\n",
 	  nestedListItem->lhs, n);

  // modify second expression and print rule and action for it
  newDefCell = newDefCell->next;
  exps = newDefCell->data->expressions;
  exps->pushBack(new expression(NONTERMINAL, strdup(nestedListItem->lhs),
				0, 0, nestedListItem));
  printYaccRule(exps, orOrSpace, yaccFile);
  orOrSpace = '|';
  fprintf(yaccFile, "            { $$ = new std::list<%s *>;\n",
	  listItem->lhs);
  fprintf(yaccFile, "              $%d->push_back($%d);\n", n, (n+1));
  fprintf(yaccFile, "              $$->push_front(new %s(",
	  defCell->data->className);
  commaFlag = 0;
  n = 1;
  for (expCell = exps->first; expCell->next->next; expCell = expCell->next)
    { // leave off last two expressions, which are nullExp and nestedListItem
      printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
    }
  fprintf(yaccFile, ")); }\n");

  // print rule and action for third expression
  newDefCell = newDefCell->next;
  exps = newDefCell->data->expressions;
  exps->removeCell(exps->last->back); // remove comma before last expression
  printYaccRule(exps, orOrSpace, yaccFile);
  fprintf(yaccFile, "            { $$ = $%d;\n", n);
  fprintf(yaccFile, "              $$->push_front(new %s(",
	  defCell->data->className);
  commaFlag = 0;
  n = 1;
  for (expCell = exps->first; expCell->next; expCell = expCell->next)
    { // leave off last expression, which is the outer list
      printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
    }
  fprintf(yaccFile, ")); }\n");
}

/********************************************************************/

/* printYaccForFixDef2

Returned Value: none

Called By: printYaccForFixList2

This is called if the fixType of a production is fixListItemsInserted2
and it is not appropriate to call printYaccForFixDef1.

The newDefs of the production have been built, and there is a new
definition of theList for each definition of listItem. The class name
of the item to instantiate in each definition is stored in the className
of the definition. The defCell argument here points to one of the
newDefs of the production. This function makes newDefs for the new
definition defCell points to by calling prepareDefsForPlain.

Example: Suppose defCell points to the following definition of
displySpecList and the className in the definition is displySpecItem_1

    device, c, DMIS, [c, vLabel], [c, displySpecList]

Then this will print:

          device, c, DMIS
	    { $$ = new std::list<displySpecItem *>;
	      $$->push_front(new displySpecItem_1($1, 0)); }
        | device C DMIS C displySpecList
            { $$ = $5;
              $$->push_front(new displySpecItem_1($1, 0)); }
        | device C DMIS C vLabel
            { $$ = new std::list<displySpecItem *>;
              $$->push_front(new displySpecItem_1($1, $5)); }
        | device C DMIS C vLabel C displySpecList
            { $$ = $7;
              $$->push_front(new displySpecItem_1($1, $5)); }
        ;

*/

void printYaccForFixDef2( /* ARGUMENTS                                 */
 char * prodName,         /* name of production being printed (a list) */
 defListCell * defCell,   /* cell pointing to definition being printed */
 char * parentName,       /* parent class name                         */
 char orOrSpace,          /* '|' or ' '                                */
 FILE * yaccFile)         /* YACC file to print in                     */
{
  defListCell * newDefCell;
  defList * newDefins;
  expList * exps;
  expListCell * expCell;
  expression * exp;
  int listIndex; // index of the list in the rule
  int commaFlag; // 1=a comma and space are needed, 0=not
  int n;

  newDefins = new defList(defCell->data->dup());
  defCell->data->newDefs = newDefins;
  prepareDefsForPlain(newDefins);
  newDefCell = newDefins->first;
  for ( ; newDefCell; newDefCell = newDefCell->next)
    {
      exps = newDefCell->data->expressions;
      printYaccRule(exps, orOrSpace, yaccFile);
      orOrSpace = '|';
      n = 1;
      commaFlag = 0;
      exp = exps->last->data;
      if ((exp->theType == NONTERMINAL) &&
	  (strcmp(prodName, exp->itemName) == 0))
	{ // definition has (recursive) list at end
	  listIndex = 0;
	  for (expCell = exps->first ; expCell; expCell = expCell->next)
	    {
	      exp = expCell->data;
	      if ((exp != nullExp)&&(exp != trueExp)&&(exp != falseExp))
		listIndex++;
	    }
	  fprintf(yaccFile, "\t    { $$ = $%d;\n", listIndex);
	  fprintf(yaccFile, "\t      $$->push_front(new %s(",
		  newDefCell->data->className);
	  for (expCell = exps->first ;
	       expCell->next; // skip the list, which is the last item
	       expCell = expCell->next)
	    {
	      printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
	    }
	  fprintf(yaccFile, ")); }\n");
	}
      else
	{ // definition does not have list at end
	  fprintf(yaccFile, "\t    { $$ = new std::list<%s *>;\n", parentName);
	  fprintf(yaccFile, "\t      $$->push_front(new %s(",
		  newDefCell->data->className);
	  for (expCell = exps->first ;
	       expCell->next; // skip the list nullExp, which is last item
	       expCell = expCell->next)
	    {
	      printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
	    }
	  fprintf(yaccFile, ")); }\n");
	}
    }
}

/********************************************************************/

/* printYaccForFixList1

Returned Value: none

Called By: printYaccProduction

This is called if the fixType of a production is fixListItemsInserted1.
The production defines a list, so it has only one definition.  The
newDefs of the definition of the production have been built. The
newDefs have a definition for each definition of the listItem of the
list. The class name of the item to instantiate in each definition is
stored in the className of the definition.

For each new definition, this function calls prepareDefsForPlain.

Example: Suppose the newDefs of the definition of snslctWristAngleList
are the following. They will have the classNames snslctWristAngleList_1,
snslctWristAngleList_2, and snslctWristAngleList_3.

    [snslctWristAngleList], stringVal, c, angle
    [snslctWristAngleList], stringVal, c, featureLabel, [c, FZ, c, angle]
    [snslctWristAngleList], stringVal, c, VEC, c, vector, [c, FZ, c, angle]

Then this will print:

snslctWristAngleList :
	  stringVal C angle C
	    { $$ = new std::list<snslctWristAngleItem *>;
	      $$->push_back(new snslctWristAngleItem_1($1, $3)); }
	| snslctWristAngleList stringVal C angle C
	    { $$ = $1;
	      $$->push_back(new snslctWristAngleItem_1($2, $4)); }
	| stringVal C featureLabel C
	    { $$ = new std::list<snslctWristAngleItem *>;
	      $$->push_back(new snslctWristAngleItem_2($1, $3, 0)); }
	| stringVal C featureLabel C FZ C angle C
	    { $$ = new std::list<snslctWristAngleItem *>;
	      $$->push_back(new snslctWristAngleItem_2($1, $3, $7)); }
	| snslctWristAngleList stringVal C featureLabel C
	    { $$ = $1;
	      $$->push_back(new snslctWristAngleItem_2($2, $4, 0)); }
	| snslctWristAngleList stringVal C featureLabel C FZ C angle C
	    { $$ = $1;
	      $$->push_back(new snslctWristAngleItem_2($2, $4, $8)); }
	| stringVal C VEC C vector C
	    { $$ = new std::list<snslctWristAngleItem *>;
	      $$->push_back(new snslctWristAngleItem_3($1, $5, 0)); }
	| stringVal C VEC C vector C FZ C angle C
	    { $$ = new std::list<snslctWristAngleItem *>;
	      $$->push_back(new snslctWristAngleItem_3($1, $5, $9)); }
	| snslctWristAngleList stringVal C VEC C vector C
	    { $$ = $1;
	      $$->push_back(new snslctWristAngleItem_3($2, $6, 0)); }
	| snslctWristAngleList stringVal C VEC C vector C FZ C angle C
	    { $$ = $1;
	      $$->push_back(new snslctWristAngleItem_3($2, $6, $10)); }
        ;

*/

void printYaccForFixList1( /* ARGUMENTS                                 */
 char * prodName,          /* name of production being printed (a list) */
 defList * defins,         /* definitions of the production             */
 FILE * yaccFile)          /* YACC file to print in                     */
{
  defList * nuDefs;         // newDefs of the production
  defListCell * defCell;
  defListCell * newDefCell;
  definition * newDef;
  defList * newDefins;
  expList * exps;
  expListCell * expCell;
  expression * exp;
  int commaFlag;            // 1=a comma and space are needed, 0=not
  char * className;
  char * parentName;        // parent class name
  int n;

  parentName = defins->first->data->expressions->last->data->itemName;
  nuDefs = defins->first->data->newDefs;
  if (!nuDefs)
    {
      fprintf(stderr, "newDefs missing in printYaccForFixList2\n");
      exit(1);
    }
  className = nuDefs->first->data->className;
  for (defCell = nuDefs->first; defCell; defCell = defCell->next)
    {
      newDef = defCell->data->dup();
      newDefins = new defList(newDef);
      defCell->data->newDefs = newDefins;
      prepareDefsForPlain(newDefins);
      newDefCell = newDefins->first;
      for ( ; newDefCell; newDefCell = newDefCell->next)
	{
	  exps = newDefCell->data->expressions;
	  printYaccRule(exps,
			((defCell->back || newDefCell->back) ? '|' : ' '),
			yaccFile);
	  commaFlag = 0;
	  exp = exps->first->data;
	  if ((exp->theType == NONTERMINAL) &&
	      (strcmp(prodName, exp->itemName) == 0))
	    { // definition has (recursive) list at the front
	      n = 2; // skip the list (first item)
	      fprintf(yaccFile, "\t    { $$ = $1;\n");
	      fprintf(yaccFile, "\t      $$->push_back(new %s(",
		      newDefCell->data->className);
	      for (expCell = exps->first->next ; // skip the list (1st item)
		   expCell;
		   expCell = expCell->next)
		{
		  printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
		}
	      fprintf(yaccFile, ")); }\n");
	    }
	  else
	    { // definition does not have list at front
	      n = 1; // the nullExp for the list (first item) does not count
	      fprintf(yaccFile, "\t    { $$ = new std::list<%s *>;\n",
		      parentName);
	      fprintf(yaccFile, "\t      $$->push_back(new %s(",
		      newDefCell->data->className);
	      for (expCell = exps->first->next; // skip the list nullExp,
		   expCell->next;
		   expCell = expCell->next)
		{
		  printYaccActionItem(expCell->data, &commaFlag, &n, yaccFile);
		}
	      fprintf(yaccFile, ")); }\n");
	    }
	}
    }
}

/********************************************************************/

/* printYaccForFixList2

Returned Value: none

Called By: printYaccProduction

This is called if the fixType of a production is fixListItemsInserted2.
The newDefs of the production have been built, and there is a new
definition of theList for each definition of listItem. The class name
of the item to instantiate in each definition is stored in the className
of the definition.

For each definition D to be printed:
Call the last expression in D END
Call the next-to-last expression in D NEXTOEND.

If the following conditions are all true, this calls printYaccForFixDef1
to do the printing. Otherwise it calls printYaccForFixDef2 to do the
printing.
1. END is an optional.
2. END has two expressions,
3. The itemName of the last of the two expressions in END is the name
   of the list being printed.
4. NEXTOEND has a prodValue that is a list; call it L.
5. One or more of the definitions of the list item for L ends
   in an optional.

The class name of the thing being listed is the itemName of the second
expression in the first (and only) definition of the list.

Example1: Suppose the newDefs of the definition of snslctWristList is
the following and it has a classNames with one entry: snslctWristItem

	  swLabel, c, snslctWristAngleList, [c, snslctWristList]
	;

Then this calls printYaccForFixDef1, which will print:

	  swLabel C snslctWristAngleItem
            { $$ = new std::list<snslctWristItem *>;
	      $$->push_front(new snslctWristItem
		     ($1, new std::list<snslctWristAngleItem *>(1, $3))); }
	| swLabel C snslctWristAngleList snslctWristAngleItem
	    { $$ = new std::list<snslctWristItem *>;
	      $3->push_back($4);
	      $$->push_front(new snslctWristItem($1, $3)); }
	| swLabel C snslctWristAngleList snslctWristList
	    { $$ = $4;
	      $$->push_front(new snslctWristItem($1, $3)); }

Example2: Suppose the newDefs of the definition of displySpecList are
the following two (see insertItemInList example) and they have
the classNames displySpecItem_1 and displySpecItem_2. 

    device, c, DMIS, [c, vLabel], [c, displySpecList]
    device, c, vLabel, [c, displySpecList]

Then this calls printYaccForFixDef2, which will print:

          device C DMIS
	    { $$ = new std::list<displySpecItem *>;
	      $$->push_front(new displySpecItem_1($1, 0)); }
        | device C DMIS C displySpecList
            { $$ = $5;
              $$->push_front(new displySpecItem_1($1, 0)); }
        | device C DMIS C vLabel
            { $$ = new std::list<displySpecItem *>;
              $$->push_front(new displySpecItem_1($1, $5)); }
        | device C DMIS C vLabel C displySpecList
            { $$ = $7;
              $$->push_front(new displySpecItem_1($1, $5)); }
        | device C vLabel
            { $$ = new std::list<displySpecItem *>;
              $$->push_front(new displySpecItem_2($1, $3)); }
        | device C vLabel C displySpecList
            { $$ = $5;
              $$->push_front(new displySpecItem_2($1, $3)); }

FIX - get rid of className; it is not doing anything.

*/

void printYaccForFixList2( /* ARGUMENTS                         */
 production * prod,        /* production being printed (a list) */
 defList * defins,         /* definitions of the production     */
 FILE * yaccFile)          /* YACC file to print in             */
{
  defList * nuDefs;          // newDefs of the production
  defListCell * defCell;
  char * className;
  char * parentName;         // list item parent class name
  char orOrSpace;
  expListCell * lastExpCell;

  parentName = defins->first->data->expressions->last->data->itemName;
  nuDefs = defins->first->data->newDefs;
  if (!nuDefs)
    {
      fprintf(stderr, "newDefs missing in printYaccForFixList2\n");
      exit(1);
    }
  className = nuDefs->first->data->className;
  for (defCell = nuDefs->first; defCell; defCell = defCell->next)
    {
      orOrSpace = (defCell->back ? '|' : ' ');
      lastExpCell = defCell->data->expressions->last;
      if ((lastExpCell->data->theType == OPTIONAL) &&
	  (lastExpCell->data->optValue->expressions->findLength() == 2) &&
	  (strcmp(prod->lhs,
	      lastExpCell->data->optValue->expressions->last->data->itemName)
	   == 0) &&
	  (lastExpCell->back->data->theType == NONTERMINAL) &&
	  (lastExpCell->back->data->prodValue->isList) &&
	  (lastExpCell->back->data->prodValue->defs->first->data->
	   expressions->last->data->prodValue->endsInOptional == true))
	printYaccForFixDef1(prod, defCell, parentName, orOrSpace, yaccFile);
      else
	printYaccForFixDef2(prod->lhs, defCell, parentName,
			    orOrSpace, yaccFile);
    }
}

/********************************************************************/

/* printYaccForListDefault

Returned Value: none

Called By: printYaccProduction

This prints the YACC for the definition of a list in default form.

If the EBNF form is:

thingList =
          [thingList], thing
        ;

Then this prints

        thingList :
        	  thing
        	    { $$ = new std::list<thing *>;
                      $$->push_back($1); }
        	| thingList thing
	            { $$ = $1;
                      $$->push_back($2); }
        	;

If the EBN form is:

thingList =
          [thingList, c], thing
        ;

Then this prints

        thingList :
        	  thing
        	    { $$ = new std::list<thing *>;
                      $$->push_back($1); }
        	| thingList C thing
	            { $$ = $1;
                      $$->push_back($3); }
        	;

It is determined whether the definition has the comma by checking if
there are 2 expressions in the optional.

*/

void printYaccForListDefault( /* ARGUMENTS                        */
 char * listName,             /* name of the list                 */
 defList * defins,            /* definitions of the production    */
 FILE * yaccFile)             /* YACC file to print in            */
{
  char * listItemName;
  expList * exps;

  exps = defins->first->data->expressions;
  listItemName = exps->first->next->data->itemName;
  fprintf(yaccFile, "\t  %s\n", listItemName);
  fprintf(yaccFile, "\t    { $$ = new std::list<%s *>;\n", listItemName);
  fprintf(yaccFile, "\t      $$->push_back($1); }\n");
  if (exps->first->data->optValue->expressions->findLength() == 2)
    {
      fprintf(yaccFile, "\t| %s C %s\n", listName, listItemName);
      fprintf(yaccFile, "\t    { $$ = $1;\n"); 
      fprintf(yaccFile, "\t      $$->push_back($3); }\n");
    }
  else
    {
      fprintf(yaccFile, "\t| %s %s\n", listName, listItemName);
      fprintf(yaccFile, "\t    { $$ = $1;\n"); 
      fprintf(yaccFile, "\t      $$->push_back($2); }\n");
    }
}

/********************************************************************/

/* printYaccForNewDefs

Returned Value: none

Called By: printYaccProduction

This is called if the fixType of production is fixProdC or fixProdCUser.
It prints the rule and action for each of list of definitions.
The newDefs of each definition already exist,

If the second expression of the expression list of a definition is
'=', and theType of the last expression is ENDLINE, and the first
expression is a label, this temporarily replaces '=' with equalSign so
that the label will be processed. After calling printYaccRule, the
'=' is put back in place before calling printYaccAction.

Otherwise, all that is necessary is to print from the expression list
in each definition by calling printYaccRule and printYaccAction.

*/

void printYaccForNewDefs( /* ARGUMENTS                                 */
 char * prodName,         /* name of production being printed (a list) */
 defList * defins,        /* definitions of the production             */
 FILE * yaccFile)         /* YACC file to print in                     */
{
  defListCell * defCell;
  defListCell * newDefCell;
  defList * newDefins;
  expList * exps;
  production * prod;
  static expression * saveExp;
  
  saveExp = 0;
  for (defCell = defins->first; defCell; defCell = defCell->next)
    {
      newDefins = defCell->data->newDefs;
      newDefCell = newDefins->first;
      for ( ; newDefCell; newDefCell = newDefCell->next)
	{
	  exps = newDefCell->data->expressions;
	  if ((exps->first) &&
	      (exps->first->next) &&
	      (exps->first->next->data->theType == ONECHAR) &&
	      (exps->first->next->data->itemName[0] == '=') &&
	      (exps->first->data->theType == NONTERMINAL) &&
	      (exps->last->data->theType == ENDLINE))
	    {
	      prod = findProd(exps->first->data->itemName);
	      if (prod && (prodIsLabel(prod) == true))
		{
		  saveExp = exps->first->next->data;
		  exps->first->next->data = equalSignExp;
		}
	    }
	  printYaccRule(exps,((defCell->back || newDefCell->back) ? '|' : ' '),
			yaccFile);
	  if (saveExp)
	    {
	      exps->first->next->data = saveExp;
	      saveExp = 0;
	    }
	  printYaccAction(prodName, newDefCell->data->className,
			  exps, yaccFile);
	}
    }
}

/********************************************************************/

/* printYaccForPlain

Returned Value: none

Called By:  printYaccProduction

This prints the YACC rules and actions for a production. The production
name is printed in printYaccProduction before this function is called.

The defins here are the defs of the production for which YACC is to be
printed.

When printing a YACC action, this prints 0, true (for trueExp),
false (for falseExp) and $N for terminals and nonterminals, but
ignores everything else. To determine the value of N, this counts
everything except NULL, trueExp, and falseExp.

For example, if the prepared definition for thing is:
 ALLSA C EXCEPT C saLabel 0 trueExp C AUTO
this will print :

   ALLSA C EXCEPT C saLabel C AUTO
   { $$ = new thing_2($5, 0, true); }

For prodCs (productions in which (1) the name is the name of another
production with a C added at the end, and (2) each definition is the
corresponding definition from the original production with a comma
added at the end), the baseClassName is the name of the production
from which the prodC was created. There is no class for the prodC.
The commas at the end of the definitions are put into the rules by
printYaccRule, but since commas (and other non-optional constants)
do not appear in class definitions, the trailing commas have no
effect on what is printed by printYaccAction.

For productions which are not prodCs, the baseClassName is the name of
the production being printed.

This modifies some productions (as follows) before calling
printYaccRule, in order to insert production names in place of
ONECHARs so that mid-rule actions will occur. Then the production
is put back the way it was before calling printYaccAction.

A. If the second expression of a definition is '=', and theType of
the last expression is ENDLINE, and the first expression is a label,
this replaces '=' with equalSign so that the label will be processed.

B. If the production being printed is boolFuncExist, this replaces '('
in the second expression with existLParen and replaces ')' in the
fourth expression with existRParen before printing the definition.
This is to prevent an undefined label which is an argument to
boolFuncExist from causing an error.

C. If the production being printed is constSgage or constSpart, this
replaces the fourth expression (which is a comma) with defCheckComma,
so that a check will be made of whether the label that precedes the
comma has been defined.

*/

void printYaccForPlain( /* ARGUMENTS                    */
 production * parent,   /* production to print YACC for */
 FILE * yaccFile)       /* YACC file to print in        */
{
  defList * defins;
  char * parentName;
  defListCell * defCell;
  defListCell * newDefCell;
  defList * newDefins;
  expList * exps;
  production * prod;
  int changeType = 0;
  static expression tempExp1;
  static expression * saveExp1;
  static expression tempExp2;
  static expression * saveExp2;

  defins = parent->defs;
  parentName = parent->lhs;
  makeNewDefs(defins);
  for (defCell = defins->first; defCell; defCell = defCell->next)
    {
      newDefins = defCell->data->newDefs;
      prepareDefsForPlain(newDefins);
      newDefCell = newDefins->first;
      for ( ; newDefCell; newDefCell = newDefCell->next)
	{
	  exps = newDefCell->data->expressions;
	  if ((exps->first) &&
	      (exps->first->next) &&
	      (exps->first->next->data->theType == ONECHAR) &&
	      (exps->first->next->data->itemName[0] == '=') &&
	      (exps->first->data->theType == NONTERMINAL) &&
	      (exps->last->data->theType == ENDLINE))
	    {
	      prod = findProd(exps->first->data->itemName);
	      if (prod && (prodIsLabel(prod) == true))
		{
		  saveExp1 = exps->first->next->data;
		  exps->first->next->data = equalSignExp;
		  changeType = 1;
		}
	    }
	  else if (strcmp(parentName, "boolFuncExist") == 0)
	    {
	      if ((defCell->data->expressions->findLength() != 4) ||
		  (exps->first->next->data->theType != ONECHAR) ||
		  (exps->first->next->next->next->data->theType != ONECHAR))
		{
		  fprintf(stderr, "Bad production for boolFuncExist\n");
		  exit(1);
		}
	      saveExp1 = exps->first->next->data;
	      saveExp2 = exps->first->next->next->next->data;
	      tempExp1.theType = NONTERMINAL;
	      tempExp1.itemName = strdup("existLParen");
	      tempExp2.theType = NONTERMINAL;
	      tempExp2.itemName = strdup("existRParen");
	      exps->first->next->data = &tempExp1;
	      exps->first->next->next->next->data = &tempExp2;
	      changeType = 2;
	    }
	  else if ((strcmp(parentName, "constSgage") == 0) ||
		   (strcmp(parentName, "constSpart") == 0))
	    {
	      if ((defins->findLength() != 1) ||
		  (exps->findLength() != 5) ||
		  (exps->first->next->next->next->data != commaExp))
		{
		  fprintf(stderr, "Bad production for %s\n", parentName);
		  exit(1);
		}
	      saveExp1 = commaExp;
	      tempExp1.theType = NONTERMINAL;
	      tempExp1.itemName = strdup("defCheckComma");
	      exps->first->next->next->next->data = &tempExp1;
	      changeType = 3;
	    }
	  printYaccRule(exps,
			((defCell->back || newDefCell->back) ? '|' : ' '),
			yaccFile);
	  if (changeType)
	    {
	      if (changeType == 1)
		{
		  exps->first->next->data = saveExp1;
		}
	      else if (changeType == 2)
		{
		  exps->first->next->data = saveExp1;
		  exps->first->next->next->next->data = saveExp2;
		}
	      else if (changeType == 3)
		{
		  exps->first->next->next->next->data = saveExp1;
		}
	      changeType = 0;
	    }
	  printYaccAction(parentName, newDefCell->data->className,
			  exps, yaccFile);
	}
    }
}

/********************************************************************/

/* printYaccForSupertype

Returned Value: none

Called By: printYaccProduction

This prints the rules and actions for all the definitions of a production
defining a supertype. For example:

snsetFeat :
	  fLabel
	    { $$ = $1; }
	| faLabel
	    { $$ = $1; }
	| datLabel
	    { $$ = $1; }
	;

*/

void printYaccForSupertype( /* ARGUMENTS                        */
 char * prodName,           /* name of production being printed */
 defList * defins,          /* definitions of the production    */
 FILE * yaccFile)           /* YACC file to print in            */
{
  char * subtypeName;
  defListCell * defCell;

  for (defCell = defins->first; defCell; defCell = defCell->next)
    {
      if (defCell->data->newDefs)
	{
	  fprintf(stderr,
		  "definition found modified in printYaccForSupertype\n");
	  exit(1);
	}
      subtypeName = defCell->data->expressions->first->data->itemName;
      fprintf(yaccFile, "\t%c %s\n", (defCell->back ? '|' : ' '), subtypeName);
      fprintf(yaccFile, "\t    { $$ = $1; }\n");
    }
}

/********************************************************************/

/* printYaccGetStatement

Returned Value: none

Called By: printYaccStart

This prints the getStatement function preceded by a lot of documentation.
Read the documentation to understand what getStatement does.

*/

void printYaccGetStatement( /* ARGUMENTS             */
 FILE * yaccFile)           /* YACC file to print in */
{
  fprintf(yaccFile,
"/* getStatement\n"
"\n"
"This gets one statement (which may cover several lines) from the\n"
"original file and stores it in bigBuf as a single line. If a statement\n"
"is obtained, this returns true. If a statement is not obtained (which\n"
"happens only when the end of the file has been reached and all\n"
"characters before the end have been handled) this returns false.\n"
"\n"
"The inner \"for\" loop reads characters from a line of the original\n"
"input file into the lilBuf and fixes the end of the line if it is\n"
"messed up. If the end of the line is messed up, a warning is given and\n"
"the end of the line is fixed so it ends correctly with <CR> followed\n"
"by <LF>. The end of the line is assumed to be intended and the end\n"
"is messed up if any of the following is seen.\n"
"1. <CR> appears without <LF> following.\n"
"2. <LF> appears without <CR> preceding.\n"
"3. EOF appears following a character that is neither <CR> nor <LF>.\n"
"\n"
"The outer \"for\" loop analyzes the line in lilBuf, and then may copy\n"
"some or all of lilBuf onto the end of bigBuf. The outer \"for\" loop\n"
"loops back only if the line in lilBuf is continued and is not followed\n"
"by a comment or a blank line.\n"
"\n"
"If a blank line follows a continued line, it ends the continuation.\n"
"\n"
"If a comment line follows a continued line, an error is recorded, and\n"
"the continuation is ended.\n"
"\n"
"This is allowing spaces and tabs before the $$ that starts a comment.\n"
"\n"
"This is allowing spaces and tabs after the $ that signifies a\n"
"continued line.\n"
"\n"
"If a line is continued, when lilBuf is copied into bigBuf, the $ that\n"
"signifies the continuation and anything following it are replaced by\n"
"<CR> followed by <LF>. If the next line is a valid continuation (not\n"
"a comment or a blank line), the <CR><LF> is overwritten by the first\n"
"two characters of the lilBuf.\n"
"\n"
"The lineNumber argument is updated to the line number in the original\n"
"file of the last line that was read.\n"
"\n"
"The extras arguments is set to the number of times the statement\n"
"stored in bigBuf was continued.\n"
"\n"
"Whenever this returns true, bigBuf ends with <CR><LF>0 .\n"
"\n"
"This checks whether either bigBuf or lilBuf is about to overflow and\n"
"exits if so.\n"
"\n"
"*/\n"
"\n"
"bool getStatement( /* ARGUMENTS                            */\n"
" FILE * original,  /* DMIS file being preprocessed         */\n"
" char * bigBuf,    /* statement buffer filled here         */\n"
" int * lineNumber, /* input file line number, updated here */\n"
" int * extras)     /* number of continuations, set here    */\n"
"{\n"
"  static char lilBuf[65540];    // input line file buffer\n"
"  int comment;                  // 1=line is a comment, 0=not\n"
"  int continued;                // 1=line continued, 0=not\n"
"  int n;                        // counter for characters in lilBuf\n"
"  int m;                        // counter for characters in bigBuf\n"
"  int k;                        // counter for spaces and tabs\n"
"  int aChar;                    // character from input file\n"
"  int blank;                    // 1=buffer is all blanks, 0=not\n"
"\n"
"  continued = 0;\n"
"  for (m = 0; ; )\n"
"    {\n"
"      blank = 1;\n"
"      for (n = 0; n < 65536; n++)\n"
"	{ // read a line of original into lilBuf and check/fix line ending\n"
"	  aChar = getc(original);\n"
"	  lilBuf[n] = aChar;\n"
"	  if (aChar == EOF)\n"
"	    {\n"
"	      if (n == 0)\n"
"		{\n"
"		  if (continued)\n"
"		    {\n"
"		      printf(\"%%d: Error - last line of file \"\n"
"			     \"is continued\\n\\n\", *lineNumber);\n"
"		      numErrors++;\n"
"		      return true; \n"
"		    }\n"
"		  return false; // done preprocessing\n"
"		}\n"
"	      else\n"
"		{\n"
"		  printf(\n"
"			  \"%%d: Error - file ends with no <LF><CR>\\n\\n\",\n"
"			  (1 + *lineNumber));\n"
"		  numErrors++;\n"
"		  lilBuf[n++] = 13;\n"
"		  lilBuf[n] = 10;\n"
"		  break;\n"
"		}\n"
"	    }\n"
"	  else if (aChar == 13)\n"
"	    {\n"
"	      aChar = getc(original);\n"
"	      if (aChar != 10)\n"
"		{\n"
"		  ungetc(aChar, original); // no harm if aChar is EOF\n"
"		  printf(\n"
"			  \"%%d: Error - <LF> is missing after <CR>\\n\\n\",\n"
"			  (1 + *lineNumber));\n"
"		  numErrors++;\n"
"		}\n"
"	      n++;\n"
"	      lilBuf[n] = 10;\n"
"	      break;\n"
"	    }\n"
"	  else if (aChar == 10)\n"
"	    {\n"
"	      printf(\n"
"		      \"%%d: Error - <CR> is missing before <LF>\\n\\n\",\n"
"		      (1 + *lineNumber));\n"
"	      numErrors++;\n"
"	      lilBuf[n++] = 13;\n"
"	      lilBuf[n] = 10;\n"
"	      break;\n"
"	    }\n"
"	  else if ((aChar != ' ') && (aChar != '\\t'))\n"
"	    blank = 0;\n"
"	}\n"
"      // line now ends with <CR><LF>\n"
"      // n is now set at the <LF>\n"
"      (*lineNumber)++;\n"
"      if (n > 65535)\n"
"	{\n"
"	  fprintf(stderr, \"Quitting: line %%d too long to handle\\n\",\n"
"		  *lineNumber);\n"
"	  exit(1);\n"
"	}\n"
"      else if (blank)\n"
"	{ // lilBuf is blank; bigBuf is ready to go if continued last time\n"
"	  if (continued)\n"
"	    {\n"
"	      return true;\n"
"	    }\n"
"	}\n"
"      else // if (!blank)\n"
"	{\n"
"	  for (k = 0; ((lilBuf[k] == ' ') || (lilBuf[k] == '\\t')); k++);\n"
"	  comment = (((lilBuf[k] == '$') && (lilBuf[k+1] == '$')) ? 1 : 0);\n"
"	  if (comment)\n"
"	    { // lilBuf is comment\n"
"	      if (continued)\n"
"		{ // if lilBuf continued last time, error & bigBuf ready\n"
"		  printf(\n"
"		  \"%%d: Error - comment line follows continued line\\n\\n\",\n"
"			  *lineNumber);\n"
"		  numErrors++;\n"
"		  return true;\n"
"		}\n"
"	    }\n"
"	  else // if (!comment)\n"
"	    { // remove <CR><LF> and spaces and tabs at end; reset continued\n"
"	      for (n = (n-2); \n"
"		   ((lilBuf[n] == ' ') || (lilBuf[n] == '\\t'));\n"
"		   n--);\n"
"	      if (lilBuf[n] == '$') // line is continued; remove $ at end\n"
"		{\n"
"		  continued = 1;\n"
"		  (*extras)++;\n"
"		  n--; \n"
"		}\n"
"	      else\n"
"		continued = 0;\n"
"	      if ((n+m+3) > 65535)\n"
"		{\n"
"		  fprintf(stderr,\n"
"			  \"Quitting: line %%d too long to handle\\n\",\n"
"			  *lineNumber);\n"
"		  exit(1);\n"
"		}\n"
"	      for (k = 0; k <= n; k++, m++)\n"
"		{ // now copy lilBuf onto end of bigBuf\n"
"		  bigBuf[m] = lilBuf[k];\n"
"		}\n"
"	      bigBuf[m] = 13;\n"
"	      bigBuf[m+1] = 10;\n"
"	      bigBuf[m+2] = 0;\n"
"	      if (!continued)\n"
"		{ // bigBuf is ready to go if lilBuf not continued\n"
"		  return true;\n"
"		}\n"
"	    }\n"
"	}\n"
"    }\n"
"  return true; // just to make compiler happy; never get here\n"
"}\n");
}

/********************************************************************/

/* printYaccGlobals

Returned Value: none

Called By: printYaccStart

This prints the extern declarations and global variables of the
YACC file.

The numErrors global variable, which is not shared with the lexer,
counts the number of errors. Whenever YACC or the preprocessor
detects an error, this is increased by one.

The numWarnings global variable, which is not shared with the lexer,
counts the number of warnings. Whenever YACC or the preprocessor
detects a possible but not definite error, this is increased by one.

*/

void printYaccGlobals( /* ARGUMENTS              */
 FILE * yaccFile)      /* YACC file to print in  */
{
  fprintf(yaccFile,
"/*\n"
"\n"
"The lineText array is used for saving everything on a line up to the end\n"
"of the line, 4096 characters, or an error, whichever comes first. The\n"
"lineText is used (printed by yyerror) only if there is an error. This \n"
"lets the user see the point in the DMIS file at which an error occurred.\n"
"Lines longer than 4096 characters will not overflow the lineText array.\n"
"\n"
"*/\n"
"\n"
"char *                     aLabel;\n"
"int                        aLabelFound = 1;\n"
"int                        aLabelType;\n"
"std::list<dmisStatement *> dmisStms;\n"
"int                        getCallArgs = 0;\n"
"int                        inDecl = 0;\n"
"stringListCell             labels[1000];\n"
"char                       lexMessage[80];\n"
"char                       lexWarning[80];\n"
"int                        lineNo;\n"
"char                       lineText[4096];\n"
"int                        macroIsReal = 1;\n"
"macroList                  macros;\n"
"int                        numErrors = 0;\n"
"int                        numMacros = 0;\n"
"int                        numWarnings = 0;\n"
"int                        resetLex = 0;\n"
"char *                     setLabel;\n"
"int                        setLabelType;\n"
"%s *                tree;\n", productions.first->data->lhs);
  fprintf(yaccFile,
"char                       warningMessage[256];\n"
"extern FILE *              yyin;\n"
"extern int                 yylex();\n");
}

/********************************************************************/

/* printYaccHandleLabel

Returned Value: none

Called By: printYaccStart

This prints in the YACC file the handleLabel function, preceded by
some documentation.

*/

void printYaccHandleLabel( /* ARGUMENTS             */
 FILE * yaccFile)          /* YACC file to print in */
{
  fprintf(yaccFile,
"/* handleLabel\n"
"\n"
"When the DMIS EXIST function is called, its argument is a label. Call\n"
"that label L.  We do not want want to signal an error when L is read,\n"
"regardless of whether or not L exists. However, reading L resets\n"
"aLabel, aLabelType, and aLabelFound.  The job of the handleLabel is to\n"
"save the current values of those global variables when the left\n"
"parenthesis before L is read (the save argument is 1 in this case) and\n"
"to restore their values when the right parenthesis following L is read\n"
"(the save argument is 0 in this case). Static local variables are used\n"
"to do the saving.\n"
"\n"
"*/\n"
"\n"
"void handleLabel(\n"
" int save)\n"
"{\n"
"  static int saveLabelFound;\n"
"  static char * saveLabel;\n"
"  static int saveLabelType;\n"
"\n"
"  if (save)\n"
"    {\n"
"      saveLabelFound = aLabelFound;\n"
"      saveLabel = aLabel;\n"
"      saveLabelType = aLabelType;\n"
"    }\n"
"  else\n"
"    {\n"
"      aLabelFound = saveLabelFound;\n"
"      aLabel = saveLabel;\n"
"      aLabelType = saveLabelType;\n"
"    }\n"
"}\n");
}

/********************************************************************/

/* printYaccIncDefs

Returned Value: none

Called By: printYaccStart

This prints the standard NIST disclaimer, #includes, and #defines in
the YACC file.

*/

void printYaccIncDefs( /* ARGUMENTS                           */
 FILE * yaccFile,      /* YACC file to print in               */
 char * baseFileName)  /* base file name for including header */
{
  fprintf(yaccFile,
"/************************************************************************\n"
"  DISCLAIMER:\n"
"  This software was produced by the National Institute of Standards\n"
"  and Technology (NIST), an agency of the U.S. government, and by statute\n"
"  is not subject to copyright in the United States.  Recipients of this\n"
"  software assume all responsibility associated with its operation,\n"
"  modification, maintenance, and subsequent redistribution.\n"
"\n"
"  See NIST Administration Manual 4.09.07 b and Appendix I.\n"
"************************************************************************/\n"
"\n"
"#include <string.h>  // for strlen, strcpy, strcat\n"
"#include <stdio.h>   // for fopen, etc.\n"
"#include <stdlib.h>  // for exit\n"
"#include \"%s.hh\"\n", baseFileName);
  fprintf(yaccFile,
"\n"
"#define YYERROR_VERBOSE\n"
"#define YYDEBUG 1\n"
"#define PRENAME \"PrEpRoCeSsDmIs\"\n");
}

/********************************************************************/

/* printYaccInsertCalledMacro

Returned Value: none

Called By: printYaccStart

This prints in the YACC file the insertCalledMacro function, preceded
by some documentation.

*/

void printYaccInsertCalledMacro( /* ARGUMENTS              */
 FILE * yaccFile)                /* YACC file to print in  */
{
  fprintf(yaccFile,
"/* insertCalledMacro\n"
"\n"
"If the number of arguments to theMacro differs from numberArgs\n"
"(the number of arguments to the call), this records an error\n"
"message, prints an ENDMAC line, and returns.\n"
"\n"
"If there are no arguments, this prints the text of theMacro, starting\n"
"with the second line.\n"
"\n"
"Otherwise, this proceeds line by line through the text of theMacro. It\n"
"copies the line into the callLine, making changes as follows. On each\n"
"line, it looks for maximum length text strings whose characters could\n"
"be a variable name. Each time it finds such a string, it checks if the\n"
"string is identical to one of the arguments of the macro. If so, the\n"
"the corresponding CALL argument is copied into the callLine instead of\n"
"the string. When the entire MACRO text line has been checked, the\n"
"callLine is printed in the preprocessed file.\n"
"\n"
"For comparison with MACRO arguments, letters in the text string are\n"
"changed to upper case.\n"
"\n"
"The line number of the CALL is printed at the beginning of every line.\n"
"\n"
"*/\n"
"\n"
"void insertCalledMacro( /* ARGUMENTS                       */\n"
" stringList * callArgs, /* arguments of the CALL           */\n"
" macroRep * theMacro,   /* called macro                    */\n"
" int numberArgs,        /* number of arguments to the CALL */\n"
" int lineNumber,        /* line number of the CALL         */\n"
" FILE * out)            /* preprocessor output file        */\n"
"{\n"
"  int n;                         // counter for arguments\n"
"  stringListCell * textCell;     // list cell for text of macro\n"
"  stringListCell * macroArgCell; // list cell for arguments of macro\n"
"  stringListCell * callArgCell;  // list cell for arguments of call\n"
"  int k;                         // counter for line of macro\n"
"  char * text;                   // line of macro text\n"
"  char word[256];                // word extracted from line of macro text\n"
"  int m;                         // counter for word\n"
"  char c;                        // current character\n"
"\n"
"  for(macroArgCell = theMacro->arguments->first, n=0;\n"
"      macroArgCell;\n"
"      macroArgCell = macroArgCell->next, n++);\n"
"  if (n != numberArgs)\n"
"    {\n"
"      fprintf(out, \"%%d|ENDMAC%%c%%c\", lineNumber, 13, 10);\n"
"      printf(\n"
"      \"%%d: Error - Numbers of CALL (%%d) and MACRO (%%d) arguments differ\\n\\n\",\n"
"	      lineNumber, numberArgs, n);\n"
"      numErrors++;\n"
"      return;\n"
"    }\n"
"  else if (numberArgs == 0)\n"
"    {\n"
"      for (textCell = theMacro->text->first->next;\n"
"	   textCell;\n"
"	   textCell = textCell->next)\n"
"	{\n"
"	  fprintf(out, \"%%d|%%s\", lineNumber, textCell->theString);\n"
"	}\n"
"      return;\n"
"    }\n"
"  for (textCell = theMacro->text->first->next;\n"
"       textCell;\n"
"       textCell = textCell->next)\n"
"    {\n"
"      fprintf(out, \"%%d|\", lineNumber);\n"
"      text = textCell->theString;\n"
"      for (k = 0; ((c = text[k]) != 0); k++)\n"
"	{\n"
"	  if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))\n"
"	    { // found the start of a word that could be a macro argument\n"
"	      for (m = 0;\n"
"		   (((c = text[k+m]) == '_') || ((c >= '0') && (c <= '9')) ||\n"
"		    ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));\n"
"		     m++)\n"
"		word[m] = (((c >= 'a') && (c <= 'z')) ? (c + ('A' - 'a')) : c);\n"
"	      word[m] = 0;\n"
"	      // found a replaceable word; replace if it is a macro arg\n"
"	      for (macroArgCell = theMacro->arguments->first,\n"
"		     callArgCell = callArgs->first;\n"
"		   macroArgCell;\n"
"		   macroArgCell = macroArgCell->next,\n"
"		     callArgCell = callArgCell->next)\n"
"		{\n"
"		  if (strcmp(macroArgCell->theString, word) == 0)\n"
"		    break;\n"
"		}\n"
"	      if (macroArgCell)\n"
"		fprintf(out, \"%%s\", callArgCell->theString);\n"
"	      else\n"
"		fprintf(out, \"%%s\", word);\n"
"	      k = (k + m - 1);\n"
"	    }\n"
"	  else if (c == '\\'')\n"
"	    { // copy quoted strings without looking for macro arguments\n"
"	      fputc(c, out);\n"
"	      for (k++; ((c = text[k]) != '\\''); k++)\n"
"		fputc(c, out);\n"
"	      fputc(c, out);\n"
"	    }\n"
"	  else\n"
"	    fputc(c, out);\n"
"	}\n"
"    }\n"
"}\n");
}

/********************************************************************/

/* printYaccIsCall

Returned Value: none

Called By: printYaccStart

This prints in the YACC file the isCall function, preceded by some
documentation.

*/

void printYaccIsCall( /* ARGUMENTS              */
 FILE * yaccFile)     /* YACC file to print in  */
{
  fprintf(yaccFile,
"/* isCall\n"
"\n"
"This returns 1 if the beginning of the text is \"CALL/M(label)\",\n"
"where there may be white space before the C, before and after the\n"
"slash, and before and after the parentheses, and the letters\n"
"in CALL may be upper or lower case.\n"
"\n"
"Otherwise, this returns 2 if the beginning of the text is\n"
"\"CALL/EXTERN,DMIS,M(label)\", with similar allowance for white space.\n"
"\n"
"Otherwise, this returns 3 if the beginning of the text is\n"
"\"CALL/EXTERN\"\n"
"\n"
"Otherwise, this returns 0.\n"
"\n"
"If this returns 1 or 2, it also stores the label in the name argument,\n"
"changing any lower case letters in the label to upper case. Otherwise,\n"
"it makes name the empty string.\n"
"\n"
"*/\n"
"\n"
"int isCall(\n"
" char * text,\n"
" char * name)\n"
"{\n"
"  int n;\n"
"  int k;\n"
"  char c;\n"
"  static char buffer[256];\n"
"\n"
"  name[0] = 0;\n"
"  for (n = 0; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if ((c != 'c') && (c != 'C'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'a') && (c != 'A'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'l') && (c != 'L'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'l') && (c != 'L'))\n"
"    return 0;\n"
"  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c != '/')\n"
"    return 0;\n"
"  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if ((c == 'm') || (c == 'M'))\n"
"    {\n"
"      for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"      if (c != '(')\n"
"	return 0;\n"
"      for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"      for (k = 0; (((c = text[n]) == '.') || (c == '-') || (c == '_') ||\n"
"		   ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'z')) ||\n"
"		   ((c >= 'A') && (c <= 'Z'))); n++)\n"
"	buffer[k++] = (((c >= 'a') && (c <= 'z')) ? (c + ('A' - 'a')) : c);\n"
"      for (; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"      if (c != ')')\n"
"	return 0;\n"
"      buffer[k] = 0;\n"
"      strcpy(name, buffer);\n"
"      return 1;\n"
"    }\n"
"  if ((c != 'e') && (c != 'E'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'x') && (c != 'X'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 't') && (c != 'T'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'e') && (c != 'E'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'r') && (c != 'R'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'n') && (c != 'N'))\n"
"    return 0;\n"
"  for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c != ',')\n"
"    return 0;\n"
"  for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if ((c != 'd') && (c != 'D'))\n"
"    return 3;\n"
"  if (((c = text[++n]) != 'm') && (c != 'M'))\n"
"    return 3;\n"
"  if (((c = text[++n]) != 'i') && (c != 'I'))\n"
"    return 3;\n"
"  if (((c = text[++n]) != 's') && (c != 'S'))\n"
"    return 3;\n"
"  for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c != ',')\n"
"    return 0;\n"
"  for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if ((c == 'm') || (c == 'M'))\n"
"       {\n"
"      for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"      if (c != '(')\n" 
"	return 0;\n"
"      for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"      for (k = 0; (((c = text[n]) == '.') || (c == '-') || (c == '_') ||\n"
"		   ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'z')) ||\n"
"		   ((c >= 'A') && (c <= 'Z'))); n++)\n"
"	buffer[k++] = (((c >= 'a') && (c <= 'z')) ? (c + ('A' - 'a')) : c);\n"
"      for (; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"      if (c != ')')\n"
"	return 0;\n"
"      buffer[k] = 0;\n"
"      strcpy(name, buffer);\n"
"      return 2;\n"
"    }\n"
"  return 3;\n"
"}\n");
}

/********************************************************************/

/* printYaccIsEndmac

Returned Value: none

Called By: printYaccStart

This prints the isEndmac function in the YACC file.

*/

void printYaccIsEndmac( /* ARGUMENTS              */
 FILE * yaccFile)       /* YACC file to print in  */
{
  fprintf(yaccFile,
"int isEndmac(\n"
" char * text)\n"
"{\n"
"  int n;\n"
"  char c;\n"
"\n"
"  for (n = 0; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if ((c != 'e') && (c != 'E'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'n') && (c != 'N'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'd') && (c != 'D'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'm') && (c != 'M'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'a') && (c != 'A'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'c') && (c != 'C'))\n"
"    return 0;\n"
"  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c != 13)\n"
"    return 0;\n"
"  return 1;\n"
"}\n");
}

/********************************************************************/

/* printYaccIsMacro

Returned Value: none

Called By: printYaccStart

This prints in the YACC file the isMacro function, preceded by some
documentation.

*/

void printYaccIsMacro( /* ARGUMENTS              */
 FILE * yaccFile)      /* YACC file to print in  */
{
  fprintf(yaccFile,
"/* isMacro\n"
"\n"
"This returns 1 if the beginning of the text is \"M(label)=MACRO\", where\n"
"there may be white space before the first M, before and after the\n"
"parentheses, and before and after the equal sign, and the letters in\n"
"MACRO may be upper or lower case.\n"
"\n"
"Otherwise, it returns 0.\n"
"\n"
"This stores the label in the name argument, changing any lower case\n"
"letters in the label to upper case.\n"
"\n"
"*/\n"
"\n"
"int isMacro(\n"
" char * text,\n"
" char ** name)\n"
"{\n"
"  int n;\n"
"  int k;\n"
"  char c;\n"
"  static char buffer[256];\n"
"\n"
"  for (n = 0; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if ((c != 'm') && (c != 'M'))\n"
"    return 0;\n"
"  for (n++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c != '(')\n"
"    return 0;\n"
"  for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c == 0)\n"
"    return 0;\n"
"  for (k = 0; (((c = text[n]) == '.') || (c == '-') || (c == '_') ||\n"
"	       ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'z')) ||\n"
"	       ((c >= 'A') && (c <= 'Z'))); n++)\n"
"    buffer[k++] = (((c >= 'a') && (c <= 'z')) ? (c + ('A' - 'a')) : c);\n"
"  for (; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c != ')')\n"
"    return 0;\n"
"  buffer[k] = 0;\n"
"  *name = strdup(buffer);\n"
"  for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if (c != '=')\n"
"    return 0;\n"
"  for (n ++; (((c = text[n]) == ' ') || (c == '\\t')); n++);\n"
"  if ((c != 'm') && (c != 'M'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'a') && (c != 'A'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'c') && (c != 'C'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'r') && (c != 'R'))\n"
"    return 0;\n"
"  if (((c = text[++n]) != 'o') && (c != 'O'))\n"
"    return 0;\n"
"  return 1;\n"
"}\n");
}

/********************************************************************/

/* printYaccLabelDefinition

Returned Value: none

Called By: printYaccProduction

This function writes YACC rules and action for label definitions. The
actions this writes include a call to doLabel, which starts the
processes of recording labels  and checking for undefined and multiply
defined labels.

WARNING: The situation with labels is messy.

The production for a label has one definition containing two
expressions. The first is a keyword whose itemName is the label type
name.  The second is a nonterminal whose itemName is either
"labelName" or "labelNameConst".

The label type name is copied into the labelTypeName local buffer.

The local variable nameName is set to the itemName of the second expression.

Printing the rule and the first line of the action is straightforward.
The rule is the (unmodified) labelTypeName followed by the nameName.
The first line of the action sets $$ to a new label of the appropriate
type. For example, if the className (production name) is "faLabel", the
labelTypeName is "FA", and the nameName is "labelName", then this prints:
	  FA labelName
          { $$ = new faLabel($2);

If the labelTypeName is DA, FA, KCA, SA, or TA, then at the time the
label is encountered in a DMIS program, it is possible that the
corresponding D, F, KC, S, or T was defined previously and the DA, FA,
KCA, SA, or TA label was defined surreptitiously by action of the DMIS
system executing the program.  So, for purposes of keeping track of
whether a label has been defined, there is only one list of labels for
D and DA, one for F and FA, one for KC and KCA, one for S and SA, and
one for T and TA. If the labelTypeName is any of those four ending in
A, after printing the rule, this knocks the A off the end so that the
combined list is used by the checking system. If reference is made to
a DA, FA, KCA, SA, or TA, and the corresponding D, F, KC, S, or T was
not defined previously, then this will let that error slip past.

If nameName is "labelNameConst", then the corresponding C++ labelNameConst 
class instance will contain the label name in its attribute a_string.

If nameName is "labelName", then different actions must be taken
according to whether the labelName production has one definition or
two. This is indicated by the labelNameTwo argument, which is true if
there are two definitions and false otherwise. In the prismatic1
conformance class, labelName has only one definition. In the full,
prismatic3, and prismatic2 conformance classes, labelName has two
definitions.

If there is only one definition of labelName it is labelNameConst.
Therefore, the labelName class instance that is $2 must be a
labelNameConst instance. In this case, that is checked (as a bug
check), and the label name is extracted by casting $2 into a
labelNameConst and getting its a_string attribute.

If there are two definitions of labelName, $2 is either a labelNameCon
(which contains a labelNameConst from which the label name may be
obtained), or $2 is a labelNameAt (representing @varName). In the latter
case, we don't know what the label name is, so we don't call the doLabel
function.

Note that this is making a new instance of a label class even if the
label already exists. In a DMIS execution system, it might be more
useful to reuse existing labels, but for building a parse tree,
building a new instance is better.

*/

void printYaccLabelDefinition( /* ARGUMENTS                            */
 char * className,             /* class name to print                  */
 expList * exps,               /* list of expressions to print         */
 bool labelNameTwo,            /* true = labelName has two definitions */
 FILE * yaccFile)              /* YACC file to print in                */
{
  static char labelTypeName[64];
  char * nameName;

  nameName = exps->first->next->data->itemName;
  strcpy(labelTypeName, exps->first->data->itemName);
  fprintf(yaccFile, "\t  %s %s\n", labelTypeName, nameName);
  if ((strlen(labelTypeName) == 2) &&
      (labelTypeName[1] == 'A') &&
      ((labelTypeName[0] == 'D') ||
       (labelTypeName[0] == 'F') ||
       (labelTypeName[0] == 'S') ||
       (labelTypeName[0] == 'T')))
    labelTypeName[1] = 0;
  else if ((labelTypeName[0] == 'K') &&
	   (labelTypeName[1] == 'C') &&
	   (labelTypeName[2] == 'A'))
    labelTypeName[2] = 0;
  fprintf(yaccFile, "\t  { $$ = new %s($2);\n", className);
  if (strcmp(nameName, "labelNameConst") == 0)
    {
      fprintf(yaccFile,
"\t    doLabel(%s, $2->get_string()); }\n", labelTypeName);
    }
  else if (labelNameTwo) // nameName must be "labelName"
    {
       fprintf(yaccFile,
"\t    if (isA($2, labelNameCon))\n"
"\t      doLabel(%s, (dynamic_cast<labelNameCon *>($2))->\n"
"\t                    get_labelNameConst()->get_string());\n"
"\t  }\n", labelTypeName); 
    }
  else // the only definition of labelName is labelNameConst
    {
      fprintf(yaccFile,
"\t    if (!(isA($2, labelNameConst)))\n"
"\t      {\n"
"\t        fprintf(stderr, \"Bug in parser\\n\");\n"
"\t        exit(1);\n"
"\t      }\n"
"\t    doLabel(%s, (dynamic_cast<labelNameConst *>($2))->get_string());\n"
"\t  }\n", labelTypeName); 
    }
}

/********************************************************************/

/* printYaccMacroClass

Returned Value: none

Called By: printYaccStart

This prints in the YACC file the macroRep class, the macroListCell
class, and the macroList class.

*/

void printYaccMacroClass( /* ARGUMENTS              */
 FILE * yaccFile)         /* YACC file to print in  */
{
  fprintf(yaccFile,
"class macroRep\n"
"{\n"
" public:\n"
"  macroRep(){text = 0; arguments = 0; name = 0;}\n"
"  macroRep(stringList * textIn, stringList * argumentsIn, char * nameIn)\n"
"    {\n"
"      text = textIn;\n"
"      arguments = argumentsIn;\n"
"      name = nameIn;\n"
"    }\n"
"  ~macroRep(){}\n"
"  stringList * text;\n"
"  stringList * arguments;\n"
"  char * name;\n"
"};\n"
"\n"
"class macroListCell\n"
"{\n"
" public:\n"
"  macroListCell(){theMacro = 0; next = 0;}\n"
"  macroListCell(macroRep * theMacroIn){theMacro = theMacroIn; next = 0;}\n"
"  ~macroListCell(){}\n"
"  macroRep * theMacro;\n"
"  macroListCell * next;\n"
"};\n"
"\n"
"class macroList\n"
"{\n"
" public:\n"
"  macroList(){first = 0; last = 0;}\n"
"  ~macroList(){}\n"
"  macroListCell * first;\n"
"  macroListCell * last;\n"
"  void pushBack(macroRep * aMacro)\n"
"  {\n"
"    if (last == 0)\n"
"      {\n"
"	last = new macroListCell(aMacro);\n"
"	first = last;\n"
"      }\n"
"    else\n"
"      {\n"
"	last->next = new macroListCell(aMacro);\n"
"	last = last->next;\n"
"      }\n"
"  }\n"
"};\n");
}

/********************************************************************/

/* printYaccMiddle

Returned Value: none

Called By: main

This prints in the YACC file:
1. the %union representing returned values from Lex
2. the %types indicating what type is returned by each production
3. the token names
4. the associative tokens that represent operators, with their precedences.
5. the %start, which is taken to be the first production in the DEBNF file.

Note that a lot of this is hard-coded. Name conflicts will occur if
the input file uses any of the following as tokens (spelled in all
upper case):
AT
BAD,
CHARSTRING
COLON
ENDLINE
EQUALS
INTSTRING
LBOX,
LBLNAME
LPAREN
RBOX
REALSTRING
RPAREN.

Bad things will happen if any of the following are used with different
meanings from what is hard-coded:
AND
EQ
GE
GT
LE
LT
MINUS
NE
NOT
OR
PLUS
POWER
SLASH

*/

void printYaccMiddle( /* ARGUMENTS             */
 FILE * yaccFile)     /* YACC file to print in */
{
  int n;
  int m;

  printYaccUnionAndTypes(yaccFile);
  for (n = 0; n < 26; n++)
    {
      for (m = 0; ((m < LETTERSIZE) && (tokenNames[n][m])); m++)
	{
	  if ((strcmp(tokenNames[n][m], "AND")) &&
	      (strcmp(tokenNames[n][m], "OR"))  &&
	      (strcmp(tokenNames[n][m], "NOT")) &&
	      (strcmp(tokenNames[n][m], "EQ"))  &&
	      (strcmp(tokenNames[n][m], "NE"))  &&
	      (strcmp(tokenNames[n][m], "LT"))  &&
	      (strcmp(tokenNames[n][m], "LE"))  &&
	      (strcmp(tokenNames[n][m], "GT"))  &&
	      (strcmp(tokenNames[n][m], "GE")))
	    {
	      fprintf(yaccFile, "%%token %s\n", tokenNames[n][m]);
	    }
	}
    }
  for (m = 0; ((m < LETTERSIZE) && (terminalNames[m])); m++)
    {
      if (strcmp(terminalNames[m], "INTSTRING") == 0)
	{
	  fprintf(yaccFile, "%%token <ival> INTSTRING\n");
	}
      else if (strcmp(terminalNames[m], "REALSTRING") == 0)
	{
	  fprintf(yaccFile, "%%token <rval> REALSTRING\n");
	}
      else
	fprintf(yaccFile, "%%token <sval> %s\n", terminalNames[m]);
    }
  fprintf(yaccFile,
	  "\n"
	  "%%token AT\n"
	  "%%token BAD\n"
	  "%%token COLON\n"
	  "%%token ENDLINE\n"
	  "%%token EQUALS\n"
	  "%%token LBOX\n"
	  "%%token LPAREN\n"
	  "%%token RBOX\n"
	  "%%token RPAREN\n"
	  "\n"
	  "%%left AND OR\n"
	  "%%left EQ NE LT LE GT GE\n"
	  "%%left PLUS MINUS\n"
	  "%%left TIMES SLASH\n"
	  "%%left POWER\n"
	  "%%left NOT\n"
	  "\n"
	  "%%start %s\n", productions.first->data->lhs);
  fprintf(yaccFile,
	  "\n"
	  "%%%%\n\n");
}

/********************************************************************/

/* printYaccParseDmis

Returned Value: none

Called By: printYaccStart

This prints the parseDmis function, which:
preprocesses the input file,
opens the preprocessed input file,
parses the preprocessed input file,
closes the preprocessed input file,
removes the preprocessed input file.

FIX - Do something about the memory used by parse trees that
are no longer accessible.

*/

void printYaccParseDmis( /* ARGUMENTS             */
 FILE * yaccFile)        /* YACC file to print in */
{
  fprintf(yaccFile,
"/* parseDmis\n"
"\n"
"This:\n"
"preprocesses the input file,\n"
"opens the preprocessed input file,\n"
"parses the preprocessed input file,\n"
"closes the preprocessed input file,\n"
"removes the preprocessed input file.\n"
"\n"
"*/\n"
"\n"
"void parseDmis(\n"
" char * fileName)\n"
"{\n"
"  lexMessage[0] = 0;\n"
"  lexWarning[0] = 0;\n"
"  preprocess(fileName);\n"
"  yyin = fopen(PRENAME, \"rb\");\n"
"  if (yyin == 0)\n"
"    {\n"
"      fprintf(stderr, \"unable to open file %%s for reading\\n\", PRENAME);\n"
"      exit(1);\n"
"    }\n"
"  yyparse();\n"
"  fclose(yyin);\n"
"  remove(PRENAME);\n"
"}\n");
}

/********************************************************************/

/* printYaccPreprocess

Returned Value: none

Called By: printYaccStart

This prints the preprocess function. This is entirely hard coded.

The preprocess reads the original DMIS file and writes a file whose
name is #defined in PRENAME (currently PrEpRoCeSsDmIs). The file
that is written by the preprocess:
1. has line continuations removed,
2. has blank lines removed,
3. has the line number from the original file included at the beginning of
   every line, followed by |. Line numbers of blank lines and continuations
   do not appear since blank lines and continuations do not occur in the
   output file.
4. has <CR> inserted before <LF> at the end of a line, if the <CR> was
   missing in the original.

*/

void printYaccPreprocess( /* ARGUMENTS             */
 FILE * yaccFile)         /* YACC file to print in */
{
  fprintf(yaccFile,
"/* preprocess\n"
"\n"
"This reads the input file and prints a preprocessed DMIS input file\n"
"in which:\n"
"1. Blank lines are removed\n"
"2. Continued lines are joined together.\n"
"3. The line number is inserted at the beginning.\n"
"\n"
"This also saves a representation of each macro that is used if the\n"
"macro is called.\n"
"\n"
"This also changes calls to macros by inserting the text of the macro after\n"
"the call, with the macro arguments replaced by the call arguments.\n"
"\n"
"This checks that the last line of the output file starts with ENDFIL\n"
"(case insensitive), possibly preceded by spaces and/or tabs. If not,\n"
"this prints an error message, adds 1 to numErrors, and adds an ENDFIL\n"
"line at the end of the file. This is necessary so that the parser\n"
"does not need to look for an unexpected file end everywhere one might\n"
"occur.\n"
"\n"
"If any of the following errors occurs, this prints an error message\n"
"and quits.\n"
"1. The named input file cannot be opened.\n"
"2. The output file cannot be opened.\n"
"\n"
"The getStatement function (1) sets lineNumber to the number of the line\n"
"from the original file last read and (2) sets extras to the number of\n"
"times the statement it reads was continued. In the output file, the\n"
"statement is assigned the number of the line on which it started, which\n"
"is (lineNumber - extras). The getStatement function returns false only\n"
"after all characters in the original file have been processed.\n"
"\n"
"*/\n"
"\n"
"void preprocess(\n"
" char * fileNameIn)\n"
"{\n"
"  FILE * original;            // input file pointer\n"
"  FILE * out;                 // output file pointer\n"
"  static char buffer[65536];  // input file buffer\n"
"  int lineNumber;             // line number of input file\n"
"  int extras;                 // number of continuations\n"
"  int n;                      // index for buffer\n"
"\n"
"  original = fopen(fileNameIn, \"rb\");\n"
"  if (original == 0)\n"
"    {\n"
"      fprintf(stderr,\n"
"	      \"could not open file %%s for reading\\n\", fileNameIn);\n"
"      exit(1);\n"
"    }\n"
"  out = fopen(PRENAME, \"wb\");\n"
"  if (out == 0)\n"
"    {\n"
"      fprintf(stderr, \"could not open file %%s for writing\\n\", PRENAME);\n"
"      exit(1);\n"
"    }\n"
"  lineNumber = 0;\n"
"  buffer[0] = 0;\n"
"  for (extras = 0; getStatement(original, buffer, &lineNumber, &extras); )\n"
"    {\n"
"      if (doCall(buffer, (lineNumber - extras), out));\n"
"      else\n"
"	{\n"
"	  fprintf(out, \"%%d|%%s\", (lineNumber - extras), buffer);\n"
"	  doMacro(buffer, (lineNumber - extras));\n"
"	}\n"
"      extras = 0;\n"
"    }\n"
"  for (n = 0; ((buffer[n] == ' ') || (buffer[n] == '\\t')); n++);\n"
"  if ((strlen(buffer) < 7) ||\n"
"      ((buffer[n  ] != 'E') && (buffer[n  ] != 'e')) ||\n"
"      ((buffer[n+1] != 'N') && (buffer[n+1] != 'n')) ||\n"
"      ((buffer[n+2] != 'D') && (buffer[n+2] != 'd')) ||\n"
"      ((buffer[n+3] != 'F') && (buffer[n+3] != 'f')) ||\n"
"      ((buffer[n+4] != 'I') && (buffer[n+4] != 'i')) ||\n"
"      ((buffer[n+5] != 'L') && (buffer[n+5] != 'l')) ||\n"
"      ((buffer[n+6] >= 'A') && (buffer[n+6] <= 'Z')) ||\n"
"      ((buffer[n+6] >= 'a') && (buffer[n+6] <= 'z')) ||\n"
"      ((buffer[n+6] >= '0') && (buffer[n+6] <= '9')))\n"
"    {\n"
"      printf(\"Error - file does not end with ENDFIL\\n\\n\");\n"
"      numErrors++;\n"
"      fprintf(out, \"%%d|ENDFIL%%c%%c\", (lineNumber+1), 13, 10);\n"
"    } \n"
"  fclose(original);\n"
"  fclose(out);\n"
"}\n");
}

/********************************************************************/

/* printYaccProduction

Returned Value: none

Called By: printYaccProductions

This prints a production. In most cases, this uses only the data in
the production, but there are exceptions.

Note that this is hard-coding the DECL and MACRO statements. If the
definition the either statement in the DEBNF for DMIS is changed,
this function will have to be changed.

1. declStm

The definitions of declStm in the production are not used to print
the YACC. Rather, what to print is hard-coded. A mid-rule action is
printed to set the inDecl global variable to 1. Also, an end-of-rule
action is printed to set inDecl back to 0.

2. callMacro

The definitions of callMacro in the production are not used to print
the YACC. Rather, what to print is hard-coded. In two places, a
mid-rule action is printed to set the getCallArgs global variable to 1.
getCallArgs is set back to 0 in lex. The effect of this is to
have lex treat the arguments to the call as an unparsable string.
This is to avoid having to deal with the ambiguity introduced by
putting labels in parentheses, which makes them look like expressions.
The arguments will already have been parsed during preprocessing.

3. A production defining a DAT or DATTRG label is handled by
printYaccDatMinor.

4. error handling

To allow the parser to continue after a line in which there is an
error, this is inserting an error handling line as the last definition
of dmisFreeStatement, dmisFirstStatement, and endfilStm. To allow the
parser to continue after a block in which there is an error, this is
also inserting an error handling line as the last definition of
endgoStm, endmesStm, endselStm, endsimreqtStm, and endxtnStm. Each of
the productions in which an error handling line is included represents
a single line of DMIS code.

There are several other DMIS commands that are not any of those that
have error handling definitions, but adding the error handling
definition line to them produces a reduce/reduce conflict with
dmisFreeStatement. In particular, caseStm, dftcasStm, endcasStm, doStm,
enddoStm, ifStm, endifStm, endmacStm, and extfilStm, have that problem.

It might be useful to improve error handling by using a different
form of error handling line that does not produce conflicts.

If any of these productions is renamed in the DEBNF for DMIS, or the
DEBNF is otherwise changed so that a single line may be represented
some other way, this function will have to be changed.

*/

void printYaccProduction( /* ARGUMENTS                            */
 production * prod,       /* production to print                  */
 bool labelNameTwo,       /* true = labelName has two definitions */
 FILE * yaccFile)         /* YACC file to print in                */
{
  char * prodName;
  defList * defins;

  prodName = prod->lhs;
  defins = prod->defs;
  fprintf(yaccFile, "%s :\n", prodName);
  if (strcmp(prodName, "declStm") == 0)
    {
      fprintf(yaccFile,
	      "\t  DECL {inDecl = 1;} SLASH declMinor ENDLINE\n"
	      "\t    { $$ = new declStm($4);\n"
	      "\t      dmisStms.push_back($$);\n"
	      "\t      inDecl = 0;\n"
	      "\t      if (lexWarning[0])\n"
	      "\t	{\n"
	      "\t	  warn(lexWarning);\n"
	      "\t	  lexWarning[0] = 0;\n"
	      "\t	}\n"
	      "\t    }\n");
     }
  else if (strcmp(prodName, "callMacro") == 0)
    {
      fprintf(yaccFile,
"	  mLabel\n"
"	    { $$ = new callMacro(false, $1, 0); }\n"
"	| mLabel C {getCallArgs = 1;} CHARSTRING\n"
"	    { $$ = new callMacro(false, $1, $4); }\n"
"	| EXTERN C DMIS C mLabel\n"
"	    { $$ = new callMacro(true, $5, 0); }\n"
"	| EXTERN C DMIS C mLabel C {getCallArgs = 1;} CHARSTRING\n"
"	    { $$ = new callMacro(true, $5, $8); }\n");
    }
  else if (strcmp(prodName, "datdefMinor") == 0)
    { // handles production defining DAT label
      if ((defins->findLength() == 1) &&
	  (defins->first->data->expressions->findLength() == 3))
	{
	  printYaccDatMinor(defins->first->data, ' ', 1, "DAT", yaccFile);
	}
      else if ((defins->findLength() == 2) &&
	       (defins->first->data->expressions->findLength() == 3) &&
	       (defins->last->data->expressions->findLength() == 5))
	{
	  printYaccDatMinor(defins->first->data, ' ', 1, "DAT", yaccFile);
	  printYaccDatMinor(defins->last->data, '|', 3, "DAT", yaccFile);
	}
      else
	{
	  fprintf(stderr, "Bad production for datdefMinor\n");
	  exit(1);
	}
    }
  else if (strcmp(prodName, "dattrgMinor") == 0)
    { // handles production defining DATTRG label
      if ((defins->findLength() != 1) ||
	  (defins->first->data->expressions->findLength() != 3))
	{
	  fprintf(stderr, "Bad production for dattrgMinor\n");
	  exit(1);
	}
      printYaccDatMinor(defins->first->data, ' ', 1, "DATTRG", yaccFile);
    }
  else if ((prod->isList) && (prod->fixType == production::fixNone))
    { // print default lists from original definition
      printYaccForListDefault(prodName, defins, yaccFile);
    }
  else if (prodIsLabel(prod) == true)
    { // handles productions defining labels except DAT, SGAGE, and SPART
      printYaccLabelDefinition(prodName, defins->first->data->expressions,
			       labelNameTwo, yaccFile);
    }
  else if (prod->isSupertype == true)
    {
      printYaccForSupertype(prodName, defins, yaccFile);
    }
  else if (prod->fixType == production::fixNone)
    {
      printYaccForPlain(prod, yaccFile);
    }
  else if (prod->fixType == production::fixListItemsInserted1)
    {
      printYaccForFixList1(prodName, defins, yaccFile);
    }
  else if (prod->fixType == production::fixListItemsInserted2)
    {
      printYaccForFixList2(prod, defins, yaccFile);
    }
  else if ((prod->fixType == production::fixProdC) ||
	   (prod->fixType == production::fixProdCUser))
    {
      printYaccForNewDefs(prodName, defins, yaccFile);
    }
  else
    {
      fprintf(stderr, "bad fixType in printYaccProduction\n");
      exit(1);
    }
  if ((strcmp(prodName, "dmisFreeStatement") == 0) ||
      (strcmp(prodName, "dmisFirstStatement") == 0) ||
      (strcmp(prodName, "endfilStm") == 0) ||
      (strcmp(prodName, "endgoStm") == 0) ||
      (strcmp(prodName, "endmesStm") == 0) ||
      (strcmp(prodName, "endselStm") == 0) ||
      (strcmp(prodName, "endsimreqtStm") == 0) ||
      (strcmp(prodName, "endxtnStm") == 0))
    {
      fprintf(yaccFile, 
"\t| error ENDLINE\n"
"\t  {\n"
"\t    numErrors++;\n"
"\t    yyerrok;\n"
"\t    aLabelFound = 1;\n"
"\t    setLabelType = 0;\n"
"\t  }\n");
    }
   fprintf(yaccFile, "\t;\n\n");
}

/********************************************************************/

/* printYaccProductions

Returned Value: none

Called By: printYacc

This prints all the productions except (1) those that define
terminalNames and (2) those that give the spelling of a keyword.

The terminalNames array, which is an array of pointers to strings that
has NULLs at the end, cannot get too big, so it is not necessary
to check for overflow here.

We are assuming that the first production is the start symbol.

For debugging purposes, we want everything in the DEBNF file
that has not been made unused by debnf2pars to have an equivalent in
the the YACC file, so that if there are unused productions in the
DEBNF, bison will find them and announce an error. The only case in
which debnf2pars makes a production unused is when a list item is
deleted. Thus, the only productions that are not printed are those
whose fixType is fixListItemDeleted.

The printYaccLabelDefinition function called by printYaccProduction
needs to know how if the labelName production has two definitions.
That is found here (so that it only needs to be done once), and
the answer is sent on.

*/

void printYaccProductions( /* ARGUMENTS             */
 FILE * yaccFile)          /* YACC file to print in */
{
  prodListCell * aCell;
  production * prod;
  char * leftName;
  int n;  // counter, also int required by findToken
  production * labelNam;
  bool labelNameTwo;

  labelNam = findProd("labelName");
  if (labelNam == 0)
    {
      fprintf(stderr, "labelName production is missing\n");
      exit(1);
    }
  labelNameTwo = (labelNam->defs->findLength() == 2);
  printYaccFirstProduction(productions.first->data, yaccFile);
  printYaccProductionsStart(yaccFile);
  for (aCell = productions.first->next; aCell; aCell = aCell->next)
    {
      prod = aCell->data;
      leftName = prod->lhs;
      for (n = 0; terminalNames[n]; n++)
	{  
	  if (!(strcmp(leftName, terminalNames[n])))
	    break;
	}
      if (terminalNames[n])
	continue; // don't process terminal names
      if (findToken(leftName, &n))
	continue; // don't process token names
      if (prod->fixType != production::fixListItemDeleted)
	printYaccProduction(prod, labelNameTwo, yaccFile);
    }
}

/********************************************************************/

/* printYaccProductionsStart

Returned Value: none

Called By: printYaccProductions

This prints the rules and actions for:
endOfLine (an alias for ENDLINE)
equalSign (an alias for EQUALS)
defCheckComma (an alias for a comma)
undefCheckComma (an alias for a comma)
existLParen (an alias for a left parenthesis)
existRParen (an alias for a right parenthesis)

The purpose of defining an alias is to provide a place to attach an
action, rather than using a mid-rule action in the production that
uses the alias.

At end of a line that is defining a label, the name of the label is in
setLabel and the type of the label (CC, F, FA, etc.) is in
setLabelType. The label is not recorded before the end of the line is
reached so that it is possible to detect if a label is used in
defining itself. If the line is not defining a label, setLabelType is
zero. Thus, at the end of a line, this checks whether setLabelType is
non-zero. If so, this records the label and sets setLabelType back to
its default value of zero.

Also at the end of a line, if any undefined labels are referenced, the
name of the first undefined label is in aLabel, and aLabelFound is set
to 0. In this case, an error message is printed and numErrors is
increased by 1.

The equalSign production is used only in label definitions, and it is
always the second expression in a label definition. If the equalSign
is seen and aLabelFound is set to 1, that means that the first
expression was a label and it has already been defined. For all labels
except F and FA, this is not allowed. So, if aLabelFound is 1 and
aLabelType is not F or FA, an error message is printed, and numErrors
is increased by 1. If aLabelFound is not 1 (it is 0), that means the
label stored in aLabel is being defined. In this case, aLabelFound
is set back to its default value of 1 (so it can be used for flagging
any undefined labels found in the rest of the definition), aLabel is
copied into setLabel, and aLabelType is copied into setLableType.

See the quoted documentation of handleLabel in the printYaccLabelHandlers
function regarding why existLParen and existRParen are used.

*/

void printYaccProductionsStart( /* ARGUMENTS             */
 FILE * yaccFile)               /* YACC file to print in */
{
  fprintf(yaccFile,
"endOfLine :\n"
"          ENDLINE\n"
"          {\n"
"            if (setLabelType)\n"
"              {\n"
"                labels[setLabelType].record(setLabel);\n"
"                setLabelType = 0;\n"
"              }\n"
"            if (aLabelFound == 0)\n"
"              {\n"
"                sprintf(warningMessage,\n"
"                         \"undefined label used?: %%s\", aLabel);\n"
"                warn(warningMessage);\n"
"                aLabelFound = 1;\n"
"              }\n"
"          }\n"
"        ;\n"
"\n"
"equalSign :\n"
"          EQUALS\n"
"          {\n"
"            if (aLabelType)\n"
"              {\n"
"                if (aLabelFound == 1)\n"
"                  {\n"
"                    if ((aLabelType != F) && (aLabelType != FA))\n"
"                      {\n"
"                        sprintf(warningMessage,\n"
"                                \"label doubly defined?: %%s\", aLabel);\n"
"                        warn(warningMessage);\n"
"                      }\n"
"                  }\n"
"                else\n"
"                  {\n"
"                    aLabelFound = 1;\n"
"                    setLabel = aLabel;\n"
"                    setLabelType = aLabelType;\n"
"                  }\n"
"              }\n"
"          }\n"
"        ;\n"
"\n");
  if (findProd("constSgage") || findProd("constSpart"))
    {
      fprintf(yaccFile,
"defCheckComma :\n"
"          C\n"
"          {\n"
"            if (aLabelFound == 1)\n"
"              {\n"
"                sprintf(warningMessage,\n"
"			\"label doubly defined?: %%s\", aLabel);\n"
"                warn(warningMessage);\n"
"              }\n"
"            else\n"
"              {\n"
"                labels[aLabelType].record(aLabel);\n"
"                aLabelFound = 1;\n"
"              }\n"
"          }\n"
"	;\n"
"\n");
    }
  fprintf(yaccFile,
"undefCheckComma :\n"
"	  C\n"
"	  {\n"
"            if (aLabelFound == 0)\n"
"              {\n"
"                sprintf(warningMessage,\n"
"                        \"undefined label used?: %%s\", aLabel);\n"
"                warn(warningMessage);\n"
"                aLabelFound = 1;\n"
"              }\n"
"          }\n"
"	;\n"
"\n");
  if (findProd("boolFuncExist"))
    {
      fprintf(yaccFile,
"existLParen :\n"
"	  LPAREN\n"
"	  { handleLabel(1); }\n"
"	;\n"
"\n"
"existRParen :\n"
"	  RPAREN\n"
"	  { handleLabel(0); }\n"
"	;\n"
"\n");
    }
}

/********************************************************************/

/* printYaccResetParser

Returned Value: none

Called By: printYaccStart

This prints in the YACC file the reportSummary function, preceded by
some documentation.

*/

void printYaccResetParser( /* ARGUMENTS             */
 FILE * yaccFile)          /* YACC file to print in */
{
  fprintf(yaccFile,
"/* resetParser\n"
"\n"
"This resets the parser by clearing out stored labels and by\n"
"resetting several variables used by the parser.\n"
"It also clears out saved macros.\n"
"\n"
"*/\n"
"\n"
"void resetParser()\n"
"{\n"
"  int n;\n"
"  stringListCell * aCell;\n"
"  stringListCell * lastCell;\n"
"  macroListCell * macroCell;\n"
"  macroListCell * prevCell;\n"
"\n"
"  numErrors = 0;\n"
"  numWarnings = 0;\n"
"  for (n = 0; n < 1000; n++)\n"
"    {\n"
"      if (labels[n].theString)\n"
"	{\n"
"	  delete labels[n].theString;\n"
"	  labels[n].theString = 0;\n"
"	  for (aCell = labels[n].next; aCell; )\n"
"	    {\n"
"	      delete aCell->theString;\n"
"	      lastCell = aCell;\n"
"	      aCell = aCell->next;\n"
"	      delete lastCell;\n"
"	    }\n"
"	  labels[n].next = 0;\n"
"	}\n"
"    }\n"
"  for (macroCell = macros.first; macroCell; )\n"
"    {\n"
"      delete macroCell->theMacro;\n"
"      prevCell = macroCell;\n"
"      macroCell = macroCell->next;\n"
"      delete prevCell;\n"
"    }\n"
"  macros.first = 0;\n"
"  macros.last = 0;\n"
"  doMacro(0,0);\n"
"  resetLex = 1;\n"
"}\n");
}

/********************************************************************/

/* printYaccRule

Returned Value: None

Called By:
   printYaccForFixList2
   printYaccForNewDefs
   printYaccForPlain

This prints a YACC rule. It prints all expressions in exps except 
nullExp, trueExp, and falseExp.

For example, if the exps are:
   ALLSA C EXCEPT C saLabel NULL trueExp C AUTO
and orOrSpace is '|' this will print :

	| ALLSA C EXCEPT C saLabel C AUTO

*/

void printYaccRule( /* ARGUMENTS                     */
 expList * exps,    /* expressions to print from     */
 char orOrSpace,    /* and OR bar '|' or a space ' ' */
 FILE * yaccFile)   /* YACC file to print in         */
{
  expListCell * expCell;
  expression * exp;

  fprintf(yaccFile, "\t%c", orOrSpace);
  expCell = exps->first;
  if (!expCell)
    fprintf(yaccFile, " /* empty */");
  for ( ; expCell; expCell = expCell->next)
    {
      exp = expCell->data;
      if ((exp != nullExp) && (exp != trueExp) && (exp != falseExp))
	{
	  printYaccExpression(exp, yaccFile);
	}
    }
  fprintf(yaccFile, "\n");
}

/********************************************************************/

/*  printYaccStart

Returned Value: none

Called By: main

This prints the beginning of the YACC file, including:
1. #includes
2. #defines
3. definitions of classes need to implement CALL
4. declarations of yyin and yylex
5. declarations of 10 other global variables (5 shared with the lexer)
6. the following functions needed to implement CALL:
   findMacro, isMacro, isCall, isEndmac, findMacroArgs, doMacro
   findCallArgs, insertCalledMacro, doCall.
7. the preprocessor functions (by calling printYaccGetStatement,
   printYaccPreprocess)
8. the function that prints a warning (by calling printYaccWarn)
9. the function that prints an error (by calling printYaccYyerror)
10. the declaration of yyparse
11. functions that deal with labels (by calling printYaccDoLabel and
    printYaccHandleLabel).
12. the function that resets the parser (by calling printYaccResetParser)
13. the function that parses a DMIS file (by calling printYaccParseDmis)

*/

void printYaccStart(  /* ARGUMENTS                           */
 FILE * yaccFile,     /* YACC file to print in               */
 char * baseFileName) /* base file name for including header */
{
  fprintf(yaccFile, "%%{\n\n");
  printYaccIncDefs(yaccFile, baseFileName);
  printStarLine(yaccFile);
  printYaccStringClasses(yaccFile);
  printStarLine(yaccFile);
  printYaccMacroClass(yaccFile);
  printStarLine(yaccFile);
  printYaccGlobals(yaccFile);
  printStarLine(yaccFile);
  printYaccFindMacro(yaccFile);
  printStarLine(yaccFile);
  printYaccIsMacro(yaccFile);
  printStarLine(yaccFile);
  printYaccIsCall(yaccFile);
  printStarLine(yaccFile);
  printYaccIsEndmac(yaccFile);
  printStarLine(yaccFile);
  printYaccFindMacroArgs(yaccFile);
  printStarLine(yaccFile);
  printYaccDoMacro(yaccFile);
  printStarLine(yaccFile);
  printYaccFindCallArgs(yaccFile);
  printStarLine(yaccFile);
  printYaccInsertCalledMacro(yaccFile);
  printStarLine(yaccFile);
  printYaccDoCall(yaccFile);
  printStarLine(yaccFile);
  printYaccGetStatement(yaccFile);
  printStarLine(yaccFile);
  printYaccPreprocess(yaccFile);
  printStarLine(yaccFile);
  printYaccWarn(yaccFile);
  printStarLine(yaccFile);
  printYaccYyerror(yaccFile);
  printStarLine(yaccFile);
  fprintf(yaccFile, "int yyparse();\n\n");
  printStarLine(yaccFile);
  printYaccDoLabel(yaccFile);
  printStarLine(yaccFile);
  printYaccHandleLabel(yaccFile);
  printStarLine(yaccFile);
  printYaccResetParser(yaccFile);
  printStarLine(yaccFile);
  printYaccParseDmis(yaccFile);
  printStarLine(yaccFile);
  fprintf(yaccFile, "%%}\n\n");
}

/********************************************************************/

/*  printYaccStringClasses

Returned Value: none

Called By: printYaccStart

This prints the stringListCell and stringList classes. Both of these
are used for dealing with macros. Only stringListCell is used for
dealing with labels.

The names of labels are kept by maintaining an array named "labels" of
stringListCells.  labels[n] is the first element of a list of labels of
type n, where n is the #defined value of the label type (given in the
C++ file generated by bison from the YACC file generated from the EBNF
file).

A stringListCell has two attributes:
1. next - a pointer to another stringListCell or to NULL at the end
2. data - a pointer to a (char *) string

A stringListCell has two constructors.

A stringListCell has two methods:
1. find - returns 1 if a string is in a list headed by "this", 0 if not.
2. record - inserts a string in a list of strings headed by "this" if
            the string is not already in the list. The new string
            is inserted in alphabetical order.

*/

void printYaccStringClasses( /* ARGUMENTS             */
 FILE * yaccFile)            /* YACC file to print in */
{
  fprintf(yaccFile,
"class stringListCell\n"
"{\n"
" public:\n"
"  stringListCell(){theString = 0; next = 0;}\n"
"  stringListCell(char * theStringIn)\n"
"    {theString = theStringIn; next = 0;}\n"
"  ~stringListCell(){}\n"
"\n"
"  int find(char * findIt)\n"
"  { // assumes list is in alphabetical order\n"
"    stringListCell * aCell;\n"
"    int compVal;\n"
"\n"
"    if (theString == 0)\n"
"      return 0;\n"
"    for (aCell = this; aCell; aCell = aCell->next)\n"
"      {\n"
"	compVal = strcmp(findIt, aCell->theString);\n"
"	if (compVal == 0)\n"
"	  return 1;\n"
"	else if (compVal < 0)\n"
"	  return 0;\n"
"      }\n"
"    return 0;\n"
"  }\n"
"\n"
"  void record(char * theStringIn)\n"
"  { // keeps list in alphabetical order, ignores duplicates\n"
"    stringListCell * aCell;\n"
"    stringListCell * newCell;\n"
"    stringListCell * backCell;\n"
"    int compVal;\n"
"\n"
"    if (theString == 0)\n"
"      { // happens when first entry is made\n"
"	theString = theStringIn;\n"
"	return;\n"
"      }\n"
"    compVal = strcmp(theStringIn, theString);\n"
"    if (compVal < 0)\n"
"      { // theStringIn comes first, so need to shift\n"
"	newCell = new stringListCell(theString);\n"
"	theString = theStringIn;\n"
"	newCell->next = next;\n"
"	next = newCell;\n"
"	return;\n"
"      }\n"
"    else if (compVal == 0)\n"
"      { // ignore duplicate\n"
"	return;\n"
"      }\n"
"    backCell = this;\n"
"    for (aCell = next; aCell; aCell = aCell->next)\n"
"      {\n"
"	compVal = strcmp(theStringIn, aCell->theString);\n"
"	if (compVal < 0)\n"
"	  { // new cell goes immediately before aCell\n"
"	    newCell = new stringListCell(theStringIn);\n"
"	    backCell->next = newCell;\n"
"	    newCell->next = aCell;\n"
"	    return;\n"
"	  }\n"
"	else if (compVal == 0)\n"
"	  { // ignore duplicate\n"
"	    return;\n"
"	  }\n"
"	backCell = aCell;\n"
"      }\n"
"    // new cell must go at the end, since we haven't returned yet\n"
"    newCell = new stringListCell(theStringIn);\n"
"    backCell->next = newCell;\n"
"  }\n"
"  \n"
"  char * theString;\n"
"  stringListCell * next;\n"
"};\n"
"\n"
"class stringList\n"
"{\n"
" public:\n"
"  stringList(){first = 0; last = 0;}\n"
"  ~stringList(){}\n"
"  stringListCell * first;\n"
"  stringListCell *last;\n"
"  void pushBack(char * text)\n"
"  {\n"
"    if (last == 0)\n"
"      {\n"
"	last = new stringListCell(text);\n"
"	first = last;\n"
"      }\n"
"    else\n"
"      {\n"
"	last->next = new stringListCell(text);\n"
"	last = last->next;\n"
"      }\n"
"  }\n"
"};\n"
"\n");
}

/********************************************************************/

/* printYaccUnionAndTypes

Called By: printYaccMiddle

This prints (1) the %union and (2) the %type list in the YACC file.
For each production in the productions list that defines a list or is
a nonterminal that is not a comma, this adds the production to the
toPrint list. Then it goes through the toPrint list twice. First it
prints the %union, then it prints the %type list. The type name of a
production used in both the %union and the %type is valN, where N is a
sequentially assigned integer starting at at 1. Every item in the
union is a pointer to either a class or a list.

A prodC is not included in the %union because no class is created for
a prodC. Rather, a prodC uses the class of the production from which it
is created. For the same reason, the %type of a prodC is the same as the
type from the prodC was created, which (conveniently) immediately
precedes the prodC in the list of productions.

*/

void printYaccUnionAndTypes( /* ARGUMENTS             */
 FILE * yaccFile)            /* YACC file to print in */
{
  prodList toPrint;
  prodListCell * prodCell;
  production * prod;
  char * prodName;
  char * itemName;
  int n;
  int j;

  for (prodCell = productions.first; prodCell; prodCell = prodCell->next)
    {
      prod = prodCell->data;
      prodName = prod->lhs;
      for (n = 0; terminalNames[n]; n++)
	{
	  if (strcmp(prodName, terminalNames[n]) == 0)
	    break;
	}
      if (terminalNames[n])
	continue; // do not process terminal names
      if (findToken(prodName, &n))
	continue; // do not process token names
      if (prod->fixType == production::fixListItemDeleted)
	continue; // do not process deleted list items
      toPrint.pushBack(prod);
    }
  
  fprintf(yaccFile, "%%union {\n");
  n = 1;
  for (prodCell = toPrint.first; prodCell; prodCell = prodCell->next)
    {
      prod = prodCell->data;
      prodName = prod->lhs;
      if (prod->isList)
	{
	  itemName =
	    prod->defs->first->data->expressions->last->data->itemName;
	  j = fprintf(yaccFile, "  std::list<%s *> ", itemName);
	  for ( ; j < 35; j++)
	    fputc(' ', yaccFile);
	  fprintf(yaccFile, "* val%d;\n", n++);
	}
      else if (prod->fixType == production::fixProdC); //no class for prodCs
      else
	{
	  j = fprintf(yaccFile, "  %s ", prodName);
	  for ( ; j < 35; j++)
	    fputc(' ', yaccFile);
	  fprintf(yaccFile, "* val%d;\n", n++);
	}
    }
  fprintf(yaccFile, "  char                             * sval;\n");
  for (n = 0; ((n < LETTERSIZE) && (terminalNames[n])); n++)
    {
      if (strcmp(terminalNames[n], "INTSTRING") == 0)
	{
	  fprintf(yaccFile, "  int                                ival;\n");
	}
      else if (strcmp(terminalNames[n], "REALSTRING") == 0)
	{
	  fprintf(yaccFile, "  double                             rval;\n");
	}
    }
  fprintf(yaccFile, "}\n\n");
  n = 1;
  for (prodCell = toPrint.first; prodCell; prodCell = prodCell->next)
    {
      prod = prodCell->data;
      prodName = prod->lhs;
      fprintf(yaccFile, "%%type <val%d> %s\n",
	      ((prod->fixType == production::fixProdC) ? (n-1) : n++),
	      prodName);
    }
  fprintf(yaccFile, "\n");
}

/********************************************************************/

/* printYaccWarn

Returned Value: none

Called By: printYaccStart

*/

void printYaccWarn( /* ARGUMENTS             */
 FILE * yaccFile)   /* YACC file to print in */
{
  fprintf(yaccFile,
"void warn(\n"
" char * s)\n"
"{\n"
"  int n;\n"
"\n"
"  printf(\"%%d Warning: %%s\\n\", lineNo, s);\n"
"  printf(\"%%s\\n\", lineText);\n"
"  for (n = 0; lineText[n] != 0; n++);\n"
"  if (lineText[n-1] != '\\n')\n"
"    printf(\"\\n\");\n"
"  numWarnings++;\n"
"}\n");
}

/********************************************************************/

/* printYaccYyerror

Returned Value: none

Called By: printYaccStart

The yyerror function this writes prints two lines. The first line
includes either an error message that may have been stored by Lex in
the lexMessage array, or an error message from the YACC parser. This
does not print both messages, since if a lexMessage is available, the
YACC message will just say "syntax error, unexpected BAD ...", which
is confusing, not helpful.  The second line is the text of the line
that caused the error, up to and including the last thing read by
Lex. The unread part of the line is not printed.

*/

void printYaccYyerror( /* ARGUMENTS             */
 FILE * yaccFile)      /* YACC file to print in */
{
  fprintf(yaccFile,
"/* yyerror\n"
"\n"
"If lexMessage contains a message, lexMessage[0] will not be 0,\n"
"and a syntax error will have occurred, since BAD will have been\n"
"returned by the lexer. In this case, only the lexMessage is\n"
"printed, since reporting the syntax error (s) only adds confusion.\n"
"\n"
"*/\n"
"\n"
"int yyerror(char * s)\n"
"{\n"
"  int n;\n"
"\n"
"  if (lexMessage[0])\n"
"    {\n"
"      printf(\"%%d: %%s\\n\", lineNo, lexMessage);\n"
"      lexMessage[0] = 0;\n"
"    }\n"
"  else\n"
"    printf(\"%%d: %%s\\n\", lineNo, s);\n"
"  printf(\"%%s\\n\", lineText);\n"
"  for (n = 0; lineText[n] != 0; n++);\n"
"  if (lineText[n-1] != '\\n')\n"
"    printf(\"\\n\");\n"
"  return 0;\n"
"}\n");
}

/********************************************************************/

/* prodIsLabel

Returned Value: bool
If all of the following are true, this returns true.
Otherwise it returns false.
a. prod has one definition.
b. that definition has two expressions.
c. the second expression is a nonterminal named labelName or labelNameConst.
d. the first expression is a token.

Called By:
  printYaccDefinitions
  printYaccProduction

*/

bool prodIsLabel(   /* ARGUMENTS            */
 production * prod) /* a production to test */
{
  expListCell * expCell;

  if ((prod->defs->first) &&
      (prod->defs->first == prod->defs->last) &&
      (prod->defs->first->data->expressions->first) &&
      (prod->defs->first->data->expressions->first->next) &&
      (prod->defs->first->data->expressions->first->next->next == 0))
    { // we now know prod has one definition and it has two expressions
      expCell = prod->defs->first->data->expressions->first;
      if ((expCell->data->theType == KEYWORD) &&
	  (expCell->next->data->theType == NONTERMINAL) &&
	  ((strcmp(expCell->next->data->itemName, "labelName") == 0) ||
	   (strcmp(expCell->next->data->itemName, "labelNameConst") == 0)))
	return true;
      else
	return false;
    }
  else
    return false;
}

/********************************************************************/

/* recordClass

Returned Value: none

Called By:
  recordClasses
  
This looks through the classDatas to see if a classData with the given
className has already been created. If so, this does nothing more.

If not, this finds the place the a new classData should go in alphabetical
order (using className), shifts already-recorded classDatas out of the way
if necessary, and puts a new classData into the right place. The new classData
has its name set to the given className and its isBlock set to the given
isBlock.

The atts and subs of the classDatas are not set here. They will be set
(if they exist) by other functions.

*/

void recordClass(                       /* ARGUMENTS                    */
 char * className,                      /* class name to record         */
 bool isBlock,                          /* true=descendant of dmisBlock */
 classData * classDatas[26][CLASSSIZE]) /* array to record name in      */
{
  int n;
  int k;
  int result;
  classData ** letterDatas;

  letterDatas = classDatas[className[0] - 'a'];
  for (n = 0; ((n < CLASSSIZE) && (letterDatas[n] != 0)); n++)
    {
      result = strcmp(className, letterDatas[n]->name);
      if (result == 0) /* already recorded */
	return;
      else if (result < 0) /* className comes before letterDatas[n] */
	break;
    }
  if (n == CLASSSIZE)
    {
      fprintf(stderr, "Too many classes starting with %c\n", className[0]);
      exit(1);
    }
  if (letterDatas[n] != 0)
    { // shift right to make room for className
      for (k = n; ((k < CLASSSIZE) && (letterDatas[k] != 0)); k++);
      if (k == CLASSSIZE)
	{
	  fprintf(stderr, "Too many classes starting with %c\n", className[0]);
	  exit(1);
	}
      for (; k >= n; k--)
	{
	  letterDatas[k] = letterDatas[k-1];
	}
    }
  letterDatas[n] = new classData(className, isBlock);
}

/********************************************************************/

/* recordClasses

Called By: main

This records in the classDatas array a classData for each class that
will be printed when classes are printed.  Each definition of each
production has a classData. All classDatas for all productions on the
toPrint list are recorded.

If a production is a supertype, this does not record classes for the
definitions, since in that case, each definition consists of a
production that is on the toPrint list (so it will get recorded when
that production is processed).

*/

void recordClasses(      /* ARGUMENTS                                      */
 prodList * toPrint,     /* list of productions for which to print classes */
 classData * classDatas[26][CLASSSIZE]) /* array to record names in        */
{
  prodListCell * prodCell;
  production * prod;
  defListCell * defCell;

  for (prodCell = toPrint->first; prodCell; prodCell = prodCell->next)
    { // gather all the class names
      prod = prodCell->data;
      recordClass(prod->lhs, findIsBlock(prod), classDatas);
      if (prod->defs->first == 0)
	{
	  fprintf(stderr, "production %s has no definitions\n", prod->lhs);
	  exit(1);
	}
      if (prod->isSupertype == false)
	{
	  for (defCell = prod->defs->first; defCell; defCell = defCell->next)
	    {
	      recordClass(defCell->data->className,
			  findIsBlock(prod), classDatas);
	    }
	}
    }
}

/********************************************************************/

/* recordTerminal

Returned Value: none

Called By:
  action for Expression

This provides a way of recording the names of all terminals so
that most of the lex file for the DMIS parser built from a debnf
input file can be generated automatically. The terminal names are
stored in alphabetical order in the terminalNames array.

*/

void recordTerminal(  /* ARGUMENTS               */
 char * terminalName) /* terminal name to record */
{
  int n;
  int k;
  int result;

  for (n = 0; ((n < LETTERSIZE) && (terminalNames[n] != 0)); n++)
    {
      result = strcmp(terminalName, terminalNames[n]);
      if (result == 0) /* already recorded */
	return;
      else if (result < 0) /* terminalName comes before terminalNames[n] */
	break;
    }
  if (n == LETTERSIZE)
    {
      fprintf(stderr, "Too many terminals\n");
      exit(1);
    }
  else if (terminalNames[n] == 0)
    {
      terminalNames[n] = terminalName;
      return;
    }
  for (k = n; ((k < LETTERSIZE) && (terminalNames[k] != 0)); k++);
  if (k == LETTERSIZE)
    {
      fprintf(stderr, "Too many terminals\n");
      exit(1);
    }
  for (; k >= n; k--)
    { // shift right to make room for terminalName
      terminalNames[k] = terminalNames[k-1];
    }
  terminalNames[n] = terminalName;
}

/********************************************************************/

/* recordToken

Returned Value: none

Called By:
  action for Expression

This provides a not horribly inefficient way of recording the
names of all tokens so that most of the lex file for the DMIS parser
built from a debnf input file can be generated automatically. The
tokenNames array is really 26 arrays (one for each first letter) of
pointers to names. The token names are stored in alphabetical order.

Some DMIS token names include digits, underscores, or minus
signs. These names are changed in the DEBNF file for DMIS to contain
only upper case letters, since EBNF does not allow any digits or minus
signs in names and does not allow a name to start with a digit. The
actual DMIS spellings of these token names, however, is included in
the DEBNF file. After the entire DEBNF file has been read, the
reviseSpelling function inserts the actual DMIS spellings of these
token names into tokenLexes.

*/

void recordToken(  /* ARGUMENTS            */
 char * tokenName) /* token name to record */
{
  int n;
  int k;
  int result;
  char ** letterNames;

  letterNames = tokenNames[tokenName[0] - 'A'];
  for (n = 0; ((n < LETTERSIZE) && (letterNames[n] != 0)); n++)
    {
      result = strcmp(tokenName, letterNames[n]);
      if (result == 0) /* already recorded */
	return;
      else if (result < 0) /* tokenName comes before letterNames[n] */
	break;
    }
  if (n == LETTERSIZE)
    {
      fprintf(stderr, "Too many tokens starting with %c\n", tokenName[0]);
      exit(1);
    }
  if (letterNames[n] != 0)
    { // shift right to make room for tokenName
      for (k = n; ((k < LETTERSIZE) && (letterNames[k] != 0)); k++);
      if (k == LETTERSIZE)
	{
	  fprintf(stderr, "Too many tokens starting with %c\n", tokenName[0]);
	  exit(1);
	}
      for (; k >= n; k--)
	{
	  letterNames[k] = letterNames[k-1];
	}
    }
  letterNames[n] = tokenName;
}

/********************************************************************/

/* removeCommaInExps

Returned Value: none

Called By:
  fixConflictListNested2
  fixConflicts1
  removeCommaInExps (recursively)

When this is called, prod should always followed by a comma. This is
checking that. If so, it removes the comma after prod wherever prod
appears, even in the expressions of an optional nested in exps. If
not, it sends and error message and quits.

*/

void removeCommaInExps( /* ARGUMENTS                                      */
 production * prod,     /* production after which to remove comma         */
 expList * exps)        /* list of expressions from which to remove comma */
{
  expListCell * expCell;

  for (expCell = exps->first; expCell; expCell = expCell->next)
    {
      if (expIsProd(expCell, prod) == true)
	{
	  if (expCell->next == 0)
	    {
	      fprintf(stderr, "Bug in removeCommaInExps");
	      exit(1);
	    }
	  else if (expIsComma(expCell->next) == true)
	    exps->removeCell(expCell->next);
	  else
	    {
	      fprintf(stderr, "Bug in removeCommaInExps");
	      exit(1);
	    }
	}
      else if (expCell->data->theType == OPTIONAL)
	{
	  removeCommaInExps(prod, expCell->data->optValue->expressions);
	}
    }
}

/********************************************************************/

/* replaceOptInDefinition

Returned Value: int
If an optional item is found, this returns 1. Otherwise, it returns 0.

Called By: replaceOptsInDefinitions

This looks at each expression in the expressions of a definition,
stopping as soon as it finds an optional item. It converts the
optional to a non-optional by calling the appropriate converter.

*/

int replaceOptInDefinition( /* ARGUMENTS                                  */
 defListCell * defCell,     /* definition to process                      */
 defList * defs)            /* list of definitions being processed        */
{
  expList * exps;        // expression list from which to remove opts
  expListCell * expCell; // cell with which to iterate
  optional * opt;        // optional to replace
  int digit;             // digit indicating type of optional

  exps = defCell->data->expressions;
  for (expCell = exps->first; expCell; expCell = expCell->next)
    {
      if (expCell->data->theType != OPTIONAL)
	continue;
      opt = expCell->data->optValue;
      if (!opt)
	{
	  fprintf(stderr, "BUG1 in replaceOptInDefinition\n");
	  exit(1);
	}
      digit = opt->digit;
      if (digit < 0)
	{
	  fprintf(stderr, "BUG2 in replaceOptInDefinition\n");
	  exit(1);
	}
      else if (digit == 0); // not currently valid, but reserved for {} list
      else // found multiplicity of size at least 1
	{ // expCell is now set to the optional to remove
	  replaceOptMulti(defCell, defs, exps, expCell, digit);
	  break;
	}
    }
  return (expCell ? 1 : 0);
}

/********************************************************************/

/* replaceOptMulti

Returned Value: none

Called By: replaceOptInDefinition

This modifies the list of definitions being examined by:

1. modifying the current definition by removing the current expression,
which is a multiplicity optional, and

2. adding one or more definitions, each of which is a copy of the
modified original definition with an additional copy of the
expressions in the optional.

First, this alters the current definition (def) so that the opt is
deleted from its list of expressions (exps). But it saves the expList
spliceEm of items in the opt. exps may be empty at this point. It also
initializes newExps by setting it to the reduced list of expressions.

Then it enters a loop. Each time around the loop, it makes newExps
bigger by making a copy of newExps and adding to it one more instance
of the items in spliceEm. Then it makes a new definition using newExps
and adds that definition to the list of definitions (defs) being
modified. The first new definition is inserted immediately after the one
being modified. The rest are each inserted immediately after the
previous one that was inserted.

Looping stops after "digit" copies of the items in spliceEm have been
added to the reduced exps.

*/

void replaceOptMulti(   /* ARGUMENTS                                     */
 defListCell * defCell, /* cell with definition being processed          */
 defList * defs,        /* list of definitions being processed           */
 expList * exps,        /* list of expressions in which to replace multi */
 expListCell * optCell, /* cell with multiple optional to replace        */
 int digit)             /* digit of optional (1 or more)                 */
{
  definition * newDef;      // new definition to be modified and inserted
  expListCell * insertCell; // cell in newExps after which to insert
  expList * spliceEm;       // expressions in the optValue of optCell
  expList * newExps;        // new expressions to be modified
  int index;                // index of insertCell
  int n;                    // counter for number of new definitions
  int m;                    // counter for finding insertCell

  spliceEm = optCell->data->optValue->expressions;
  for (index = 0, insertCell = exps->first; // insertCell is used as a dummy
       (insertCell != optCell);             // here in order to find index
       index++, insertCell = insertCell->next);
  exps->removeCell(optCell);  // modify original by removing optCell
  newExps = exps;             // initialize resetting of exps (may be empty)
  for (n = 0; n < digit; n++)
    { 
      newExps = newExps->dup();
      for (m = 0, insertCell = newExps->first;
	   ((m < index) && insertCell);
	   m++, insertCell = insertCell->next);
      if (insertCell == 0) 
	insertCell = newExps->last;
      else
	insertCell = insertCell->back;
      newExps->spliceInAfterCell(insertCell, spliceEm);
      newDef = new definition(0, newExps, 0);
      defs->insertAfterCell(defCell, newDef);
      defCell = defCell->next;
    }
}

/********************************************************************/

/* replaceOptsInDefinitions

Returned Value: none

Called By:
  fixConflictOther
  makeProdC


This function replaces any optionals found in the expressions of any
definition in the list of definitions that is the defs argument. It
does this by adding definitions to the list.

This function works by calling replaceOptInDefinition repeatedly to
replace one optional in one definition each time. Whenever
replaceOptInDefinition replaces an optional, it returns 1. As long as
replaceOptInDefinition returns 1, this function calls it again for the
same definition. When all optionals in one definition have been
replaced, replaceOptInDefinition returns 0 and this function goes on
to the next definition. This technique deals nicely both with nested
optionals and with several optionals in one definition. 

This function has the following interesting feature: since any
definition after the first may have been added by the processing of
the previous definition, the list of definitions being traversed is
modified while it is being traversed.

As a simple example, pointRange is changed from the first production
below to the second.

pointRange :
	| LBOX intVal [C intVal] RBOX
	;

pointRange :
	  LBOX intVal RBOX
	| LBOX intVal C intVal RBOX
	;

In a more complex example the following production would be modified
in the stages shown. The long arrows on the left ---> indicate the
modified original. The short arrows on the left --> indicate the
modified copy. The arrows on the right <--- point to the line that
will be expanded next.

deleteAllSensors :
	  ALLSA [C EXCEPT C saLabel [C sxLabel]] [C didLabel]

*****************************

deleteAllSensors :
--->      ALLSA [C didLabel] <---
-->	| ALLSA C EXCEPT C saLabel [C sxLabel] [C didLabel]

*****************************

deleteAllSensors :
--->      ALLSA
-->     | ALLSA C didLabel
	| ALLSA C EXCEPT C saLabel [C sxLabel] [C didLabel] <---


*****************************

deleteAllSensors :
          ALLSA
        | ALLSA C didLabel
--->	| ALLSA C EXCEPT C saLabel [C didLabel] <---
-->	| ALLSA C EXCEPT C saLabel C sxLabel [C didLabel]

*****************************

deleteAllSensors :
          ALLSA
        | ALLSA C didLabel
--->	| ALLSA C EXCEPT C saLabel
-->	| ALLSA C EXCEPT C saLabel C didLabel
	| ALLSA C EXCEPT C saLabel C sxLabel [C didLabel] <---

*****************************

deleteAllSensors :
          ALLSA
        | ALLSA C didLabel
	| ALLSA C EXCEPT C saLabel
	| ALLSA C EXCEPT C saLabel C didLabel
--->	| ALLSA C EXCEPT C saLabel C sxLabel
-->	| ALLSA C EXCEPT C saLabel C sxLabel C didLabel

*****************************

See the documentation of replaceOptMulti for an example of replacing
a multiple optional (one of the form n*[foo]).

Replacing optionals may result in duplicate definitions if DMIS
contains an ambiguity or the EBNF for DMIS has been written in such a
way as to create the appearance of ambiguity (for example if 2*[opt]
is written as [opt],[opt]) so checkDuplicates is called to look for
duplicates. If a duplicate is found, checkDuplicates prints an error
message and exits. If duplicates were allowed, bison would give a
warning but build a defective parser.

*/

void replaceOptsInDefinitions( /* ARGUMENTS                                 */
 defList * defs)               /* definitions in which to replace optionals */
{
  defListCell * defCell;

  for (defCell = defs->first; defCell; defCell = defCell->next)
    {
      while (replaceOptInDefinition(defCell, defs));
    }
  checkDuplicates(defs);
}

/********************************************************************/

/* replaceProdC

Returned Value: None

Called By:
  fixConflictOther1
  fixConflictOther2

This replaces all occurrences of "prod, C" in all definitions on the
defins list with prodC. Also, if replaceEnd is true, the last
expression E in each definition that is not is nullExp, falseExp, or
trueExp is replaced by prodC if E is prod.

Any occurrences of nullExp, falseExp, or trueExp are ignored in
deciding whether prod is followed C. For example, 
  prod, falseExp, trueExp, C, bleep
would be changed to:
  prodC, falseExp, trueExp, bleep

Having to treat falseExp, trueExp, and nullExp as if they were not
there makes the code a little strange.

*/

void replaceProdC(   /*  ARGUMENTS                                */
 defList * defins,   /* definitions in which to make replacements */
 production * prod,  /* production used by user                   */
 production * prodC, /* replacement for "prod, C"                */
 bool replaceEnd)    /* flag for replacing at end                 */
{
  defListCell * defCell;
  expListCell * expCell;
  expListCell * expCell2;

  for (defCell = defins->first; defCell; defCell = defCell->next)
    { // replace "prod, C" with prodC in all definitions of user
      expCell = defCell->data->expressions->first;
      if (expCell == 0) // handles empty definition
	continue;
      for ( ; expCell->next; )
	{
	  if (expIsProd(expCell, prod) == true)
	    {
	      for(expCell2 = expCell->next;
		  expCell2;
		  expCell2 = expCell2->next)

		{
		  if ((expCell2->data != trueExp) &&
		      (expCell2->data != falseExp) &&
		      (expCell2->data != nullExp))
		    break;
		}
	      if (expCell2 == 0)
		{ // expCell->data is now the last printable expr in the def
		  break;
		}
	      else if (expIsComma(expCell2) == true)
		{ // found "prod, C"
		  // replace prod with prodC
		  expCell->data->itemName = strdup(prodC->lhs);
		  expCell->data->prodValue = prodC;
		  // remove the comma cell from the list of expressions
		  defCell->data->expressions->removeCell(expCell2);
		}
	      expCell = expCell->next;  // need to set and test here since
	      if (expCell == 0)         // loop test tests expCell->next
		break;
	    }
	  else
	    expCell = expCell->next;
	}
      if (expCell && (replaceEnd == true) &&
	  (expIsProd(expCell, prod) == true))
	{ // expCell->data is now the last printable expression in the def
	  expCell->data->theType = NONTERMINAL;
	  expCell->data->itemName = strdup(prodC->lhs);
	  expCell->data->prodValue = prodC;
	}
    }
}

/********************************************************************/

/* reviseSpelling

Returned Value: None

Called By: main

First, this makes each pointer in the tokenLexes array point to the
same name that is pointed at by the corresponding element of tokenNames
(so there are two pointers to the same thing).

Then this looks at all the productions. For each production this calls
findToken to see if the left side of the production is the name of a
token. If it is the name of a token, findToken records the position in
tokenNames at which it was found. If any production has a token on the
left side, the right side should give the correct spelling to look for
in Lex in order to recognize the token. The right side should consist
of a single definition containing expressions that are all either type
ONECHAR or type TWOCHAR. The correct string to look for in Lex is formed
and stored in the position of tokenLexes corresponding to the position
in tokenNames at which the token was found.

If the contents of the right side of a production defining a token
are not what they should be, an error message is printed and
debnf2pars exits.

*/

void reviseSpelling() /* NO ARGUMENTS */
{
  prodListCell * aCell;       // a prodListCell to examine
  char * leftName;            // the name on the left side of a production
  int n;                      // index of position in tokenLexes
  int k;                      // index for buffer
  defList * defs;             // the definition to examine
  expList * exps;             // the expressions to examine
  expListCell * expCell;      // expListCell to examine
  expression * exp;           // an expression to examine
  
  for (k = 0; k < 26; k++)
    {
      for (n = 0; ((n < LETTERSIZE) && (tokenNames[k][n])); n++)
	tokenLexes[k][n] = tokenNames[k][n];
    }
  for (aCell = productions.first; aCell; aCell = aCell->next)
    {
      leftName = aCell->data->lhs;
      if (findToken(leftName, &n))
	{
	  defs = aCell->data->defs;
	  if ((defs->first == 0) || (defs->first != defs->last))
	    { // not exactly one definition
	      fprintf(stderr, "Bad token spelling in reviseSpelling\n");
	      exit(1);
	    }
	  exps = defs->first->data->expressions;
	  k = 0;
	  for (expCell = exps->first; expCell; expCell = expCell->next)
	    {
	      exp = expCell->data;
	      if (exp->theType == ONECHAR)
	        buffer[k++] = exp->itemName[0];
	      else if (exp->theType == TWOCHAR)
		{
		  if (exp->itemName[1] != (exp->itemName[0] + ('a' - 'A')))
		    { // second letter not lower case of first (upper) letter
		      fprintf(stderr,
			      "Bad token spelling in reviseSpelling\n");
		      exit(1);
		    }
		  buffer[k++] = exp->itemName[0];
		}
	      else
		{ // not a ONECHAR or a TWOCHAR
		  fprintf(stderr, "Bad token spelling in reviseSpelling\n");
		  exit(1);
		}
	    }
	  buffer[k] = 0;
	  tokenLexes[leftName[0] - 'A'][n] = strdup(buffer);
	}
    }
}

/********************************************************************/

/* selectProductions

Called By: main

This puts all the productions for which classes should be printed into
the toPrint list. The productions which should be printed are all
except:
1. the nonterminal for a comma
2. terminal names
3. token names
4. lists.

The terminalNames array, which is an array of pointers to strings that
has NULLs at the end, cannot get too big, so it is not necessary
to check for overflow here.

*/
void selectProductions( /* ARGUMENTS                                  */
 prodList * toPrint)    /* list of productions to process, built here */
{
  prodListCell * prodCell;
  production * prod;
  char * prodName;
  int n;
  
  for (prodCell = productions.first; prodCell; prodCell = prodCell->next)
    {
      prod = prodCell->data;
      prodName = prod->lhs;
      for (n = 0; terminalNames[n]; n++)
	{
	  if (strcmp(prodName, terminalNames[n]) == 0)
	    break;
	}
      if (terminalNames[n])
	continue; // do not process terminal names
      if (findToken(prodName, &n))
	continue; // do not process token names
      if (prod->isList)
	continue; // do not process lists
      toPrint->pushBack(prod);
    }
}

/********************************************************************/

/* yyerror

Returned Value: int (0)

Called By: yyparse

This prints whatever string the parser provides.

*/

int yyerror( /* ARGUMENTS       */
 char * s)   /* string to print */
{
  fprintf(stderr, "%s\n", s);
  return 0;
}

/********************************************************************/


